]> git.sesse.net Git - nageru/blobdiff - nageru/mixer.cpp
Increase the master card timeout to 200 ms.
[nageru] / nageru / mixer.cpp
index 3b7a99c0319ea567a0f5fcb38f17075ef9d7151d..cbca327e07663326a0eee26512939d093f1f59e3 100644 (file)
@@ -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<mutex> lock(card_mutex);
+                       handle_hotplugged_cards();
+               }
 
                vector<int32_t> 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,38 @@ pair<string, string> 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<int32_t> raw_audio[MAX_VIDEO_CARDS])
 {
        OutputFrameInfo output_frame_info;
+       constexpr steady_clock::duration master_card_timeout = milliseconds(200);
 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) {
+                       fprintf(stderr, "WARNING: Master card (%s) did not deliver a frame for %u ms, creating a fake one.\n",
+                               description_for_card(master_card_index).c_str(),
+                               unsigned(duration_cast<milliseconds>(master_card_timeout).count()));
+               }
+       }
+
+       if (timed_out) {
+               // The master card stalled for 200 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 +1537,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<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;
@@ -1548,6 +1577,14 @@ start:
        fractional_samples = num_samples_times_timebase % TIMEBASE;
        assert(output_frame_info.num_samples >= 0);
 
+       if (timed_out) {
+               DeviceSpec device{InputSourceType::CAPTURE_CARD, master_card_index};
+               bool success;
+               do {
+                       success = audio_mixer->add_silence(device, output_frame_info.num_samples, /*dropped_frames=*/0);
+               } while (!success);
+       }
+
        return output_frame_info;
 }