CaptureCard *card = &cards[card_index];
if (card->capture != nullptr) {
+ card_mutex.unlock(); // The dequeue thread could be waiting for bm_frame().
card->capture->stop_dequeue_thread();
+ card_mutex.lock();
}
card->capture.reset(capture);
card->is_fake_capture = (card_type == CardType::FAKE_CAPTURE);
assert(card_type == CardType::FFMPEG_INPUT);
}
- DeviceSpec device;
- device = DeviceSpec{InputSourceType::CAPTURE_CARD, card_index};
- audio_mixer->reset_resampler(device);
+ DeviceSpec device{InputSourceType::CAPTURE_CARD, card_index};
unsigned num_channels = card_type == CardType::LIVE_CARD ? 8 : 2;
if (is_active) {
audio_mixer->set_device_parameters(device, card->capture->get_description(), card_type, num_channels, /*active=*/true);
snprintf(name, sizeof(name), "Fake card %u", card_index + 1);
audio_mixer->set_device_parameters(device, name, card_type, num_channels, /*active=*/false);
}
+ audio_mixer->reset_resampler(device);
audio_mixer->trigger_state_changed_callback();
// Unregister old metrics, if any.
// If the first card is reporting a corrupted or otherwise dropped frame,
// just increase the pts (skipping over this frame) and don't try to compute anything new.
- if (!master_card_is_output && new_frames[master_card_index].frame->len == 0) {
+ if (!master_card_is_output &&
+ new_frames[master_card_index].frame != nullptr && // Timeout.
+ new_frames[master_card_index].frame->len == 0) {
++stats_dropped_frames;
pts_int += new_frames[master_card_index].length;
continue;
Mixer::OutputFrameInfo Mixer::get_one_frame_from_each_card(unsigned master_card_index, bool master_card_is_output, CaptureCard::NewFrame new_frames[MAX_VIDEO_CARDS], bool has_new_frame[MAX_VIDEO_CARDS], vector<int32_t> raw_audio[MAX_VIDEO_CARDS])
{
OutputFrameInfo output_frame_info;
+ constexpr steady_clock::duration master_card_timeout = milliseconds(100);
start:
unique_lock<mutex> lock(card_mutex, defer_lock);
+ bool timed_out = false;
if (master_card_is_output) {
// Clocked to the output, so wait for it to be ready for the next frame.
cards[master_card_index].output->wait_for_frame(pts_int, &output_frame_info.dropped_frames, &output_frame_info.frame_duration, &output_frame_info.is_preroll, &output_frame_info.frame_timestamp);
lock.lock();
} else {
// Wait for the master card to have a new frame.
- // TODO: Add a timeout.
output_frame_info.is_preroll = false;
lock.lock();
- cards[master_card_index].new_frames_changed.wait(lock, [this, master_card_index]{ return !cards[master_card_index].new_frames.empty() || cards[master_card_index].capture->get_disconnected(); });
- }
-
- if (master_card_is_output) {
+ timed_out = !cards[master_card_index].new_frames_changed.wait_for(lock,
+ master_card_timeout,
+ [this, master_card_index]{
+ return !cards[master_card_index].new_frames.empty() ||
+ cards[master_card_index].capture->get_disconnected();
+ });
+ }
+
+ if (timed_out) {
+ // The master card stalled for 100 ms (possible when it's e.g.
+ // an SRT card). Send a frame no matter what; this also makes sure
+ // any other cards get to empty their queues, and in general,
+ // that we make _some_ sort of forward progress.
+ handle_hotplugged_cards();
+ } else if (master_card_is_output) {
handle_hotplugged_cards();
} else if (cards[master_card_index].new_frames.empty()) {
// We were woken up, but not due to a new frame. Deal with it
raw_audio[card_index] = move(card->new_raw_audio);
}
- if (!master_card_is_output) {
+ if (timed_out) {
+ // Pretend the frame happened a while ago and was only processed now,
+ // so that we get the duration sort-of right. This isn't ideal.
+ output_frame_info.dropped_frames = 0; // Hard to define, really.
+ output_frame_info.frame_duration = lrint(TIMEBASE * duration<double>(master_card_timeout).count());
+ output_frame_info.frame_timestamp = steady_clock::now() - master_card_timeout;
+ } else if (!master_card_is_output) {
output_frame_info.frame_timestamp = new_frames[master_card_index].received_timestamp;
output_frame_info.dropped_frames = new_frames[master_card_index].dropped_frames;
output_frame_info.frame_duration = new_frames[master_card_index].length;