+void Mixer::handle_hotplugged_cards()
+{
+ // Check for cards that have been disconnected since last frame.
+ for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
+ CaptureCard *card = &cards[card_index];
+ if (card->capture->get_disconnected()) {
+ fprintf(stderr, "Card %u went away, replacing with a fake card.\n", card_index);
+ configure_card(card_index, new FakeCapture(card_index), /*is_fake_capture=*/true);
+ card->queue_length_policy.reset(card_index);
+ card->capture->start_bm_capture();
+ }
+ }
+
+ // Check for cards that have been connected since last frame.
+ vector<libusb_device *> hotplugged_cards_copy;
+ {
+ lock_guard<mutex> lock(hotplug_mutex);
+ swap(hotplugged_cards, hotplugged_cards_copy);
+ }
+ for (libusb_device *new_dev : hotplugged_cards_copy) {
+ // Look for a fake capture card where we can stick this in.
+ int free_card_index = -1;
+ for (unsigned card_index = 0; card_index < num_cards; ++card_index) {
+ if (cards[card_index].is_fake_capture) {
+ free_card_index = int(card_index);
+ break;
+ }
+ }
+
+ if (free_card_index == -1) {
+ fprintf(stderr, "New card plugged in, but no free slots -- ignoring.\n");
+ libusb_unref_device(new_dev);
+ } else {
+ // BMUSBCapture takes ownership.
+ fprintf(stderr, "New card plugged in, choosing slot %d.\n", free_card_index);
+ CaptureCard *card = &cards[free_card_index];
+ BMUSBCapture *capture = new BMUSBCapture(free_card_index, new_dev);
+ configure_card(free_card_index, capture, /*is_fake_capture=*/false);
+ card->queue_length_policy.reset(free_card_index);
+ capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, free_card_index));
+ capture->start_bm_capture();
+ }
+ }
+}
+
+
+void Mixer::schedule_audio_resampling_tasks(unsigned dropped_frames, int num_samples_per_frame, int length_per_frame)
+{
+ // Resample the audio as needed, including from previously dropped frames.
+ assert(num_cards > 0);
+ for (unsigned frame_num = 0; frame_num < dropped_frames + 1; ++frame_num) {
+ {
+ // Signal to the audio thread to process this frame.
+ unique_lock<mutex> lock(audio_mutex);
+ audio_task_queue.push(AudioTask{pts_int, num_samples_per_frame});
+ audio_task_queue_changed.notify_one();
+ }
+ if (frame_num != dropped_frames) {
+ // For dropped frames, increase the pts. Note that if the format changed
+ // in the meantime, we have no way of detecting that; we just have to
+ // assume the frame length is always the same.
+ pts_int += length_per_frame;
+ }
+ }
+}
+
+void Mixer::render_one_frame(int64_t duration)