+void Mixer::set_output_card_internal(int card_index)
+{
+ // We don't really need to take card_mutex, since we're in the mixer
+ // thread and don't mess with any queues (which is the only thing that happens
+ // from other threads), but it's probably the safest in the long run.
+ unique_lock<mutex> lock(card_mutex);
+ if (output_card_index != -1) {
+ // Switch the old card from output to input.
+ CaptureCard *old_card = &cards[output_card_index];
+ old_card->output->end_output();
+
+ // Stop the fake card that we put into place.
+ // This needs to _not_ happen under the mutex, to avoid deadlock
+ // (delivering the last frame needs to take the mutex).
+ bmusb::CaptureInterface *fake_capture = old_card->capture.get();
+ lock.unlock();
+ fake_capture->stop_dequeue_thread();
+ lock.lock();
+ old_card->capture = move(old_card->parked_capture);
+ old_card->is_fake_capture = false;
+ old_card->capture->start_bm_capture();
+ }
+ if (card_index != -1) {
+ CaptureCard *card = &cards[card_index];
+ bmusb::CaptureInterface *capture = card->capture.get();
+ // TODO: DeckLinkCapture::stop_dequeue_thread can actually take
+ // several seconds to complete (blocking on DisableVideoInput);
+ // see if we can maybe do it asynchronously.
+ lock.unlock();
+ capture->stop_dequeue_thread();
+ lock.lock();
+ card->parked_capture = move(card->capture);
+ bmusb::CaptureInterface *fake_capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
+ configure_card(card_index, fake_capture, /*is_fake_capture=*/true, card->output.release());
+ card->queue_length_policy.reset(card_index);
+ card->capture->start_bm_capture();
+ desired_output_video_mode = output_video_mode = card->output->pick_video_mode(desired_output_video_mode);
+ card->output->start_output(desired_output_video_mode, pts_int);
+ }
+ output_card_index = card_index;
+}