}
}
+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)
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)));
}
}
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);
+ double extra_delay_ms = new_extra_delay_ms[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]) {
+ 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;
alsa_pool.reset_device(device_spec.index);
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 < 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));
}
}
const double input_samples_consumed = total_consumed_samples +
num_samples / (ratio * rcorr);
- double actual_delay = input_samples_received - input_samples_consumed;
+ double actual_delay = input_samples_received - input_samples_consumed + adjustment_samples;
actual_delay += vresampler.inpdist(); // Delay in the resampler itself.
double err = actual_delay - expected_delay;
if (first_output) {
}
return true;
}
+
+void ResamplingQueue::change_expected_delay(double expected_delay_seconds)
+{
+ double new_delay = expected_delay_seconds * OUTPUT_FREQUENCY;
+
+ if (a1.input_samples_received == 0) {
+ // Before the first block, so no need to adjust the queue.
+ expected_delay = new_delay;
+ return;
+ }
+
+ int samples_to_add = lrint(new_delay - expected_delay);
+ if (samples_to_add > 0) {
+ adjustment_samples += samples_to_add;
+ buffer.insert(buffer.end(), samples_to_add, 0.0f);
+ } else if (samples_to_add < 0) {
+ int samples_to_remove = min<int>(-samples_to_add, buffer.size());
+ buffer.erase(buffer.begin(), buffer.begin() + samples_to_remove);
+ adjustment_samples -= samples_to_remove;
+ }
+ expected_delay = new_delay;
+}
void add_input_samples(std::chrono::steady_clock::time_point ts, const float *samples, ssize_t num_samples, RateAdjustmentPolicy rate_adjustment_policy);
// Returns false if underrun.
bool get_output_samples(std::chrono::steady_clock::time_point ts, float *samples, ssize_t num_samples, RateAdjustmentPolicy rate_adjustment_policy);
+ // Instantly change delay by removing samples, or adding zeros.
+ // Does not correct a queue that is in bad shape; e.g., if you change
+ // delay from 100 to 110 ms but your queue had a real delay of 200 ms,
+ // 10 ms worth of zeros will be inserted nevertheless.
+ void change_expected_delay(double expected_delay_seconds);
private:
void init_loop_filter(double bandwidth_hz);
};
InputPoint a0, a1;
+ // If nonzero, how many samples we've added or removed as part of change_expected_delay().
+ int adjustment_samples = 0;
+
// The current rate at which we seem to get input samples, in Hz.
// For an ideal input, identical to freq_in.
double current_estimated_freq_in;
// How much delay we are expected to have, in input samples.
// If actual delay drifts too much away from this, we will start
// changing the resampling ratio to compensate.
- const double expected_delay;
+ double expected_delay;
// Input samples not yet fed into the resampler.
// TODO: Use a circular buffer instead, for efficiency.