X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=nageru%2Fmixer.cpp;h=26296180ac1371a0f14486849d451639d7a3d092;hb=6c812cc37cb9cb97b22f1d0de02289d5fae2ac00;hp=0efed5aa2d89137eb1bcc7a3baa2be7895984f04;hpb=3d7ae61cc383a344b2458bd47ab13ce436311eb1;p=nageru diff --git a/nageru/mixer.cpp b/nageru/mixer.cpp index 0efed5a..2629618 100644 --- a/nageru/mixer.cpp +++ b/nageru/mixer.cpp @@ -574,7 +574,9 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardT 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); @@ -629,9 +631,7 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardT 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); @@ -641,6 +641,7 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardT 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. @@ -1289,7 +1290,10 @@ void Mixer::thread_func() assert(master_card_index < MAX_VIDEO_CARDS); } - handle_hotplugged_cards(); + { + lock_guard lock(card_mutex); + handle_hotplugged_cards(); + } vector raw_audio[MAX_VIDEO_CARDS]; // For MJPEG encoding. OutputFrameInfo output_frame_info = get_one_frame_from_each_card(master_card_index, master_card_is_output, new_frames, has_new_frame, raw_audio); @@ -1311,7 +1315,9 @@ void Mixer::thread_func() // 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; @@ -1465,21 +1471,37 @@ pair Mixer::get_channel_color_http(unsigned channel_idx) 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 raw_audio[MAX_VIDEO_CARDS]) { OutputFrameInfo output_frame_info; + constexpr steady_clock::duration master_card_timeout = milliseconds(100); start: unique_lock 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) { + fprintf(stderr, "WARNING: Master card (%s) did not deliver a frame for 100 ms, creating a fake one.\n", + description_for_card(master_card_index).c_str()); + } + } + + 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 @@ -1514,7 +1536,13 @@ start: 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(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; @@ -2159,6 +2187,12 @@ void Mixer::start_srt() } break; } + if (!global_flags.enable_srt) { // Runtime UI toggle. + // Perhaps not as good as never listening in the first place, + // but much simpler to turn on and off. + srt_close(clientsock); + continue; + } lock_guard lock(hotplug_mutex); hotplugged_srt_cards.push_back(clientsock); }