]> git.sesse.net Git - nageru/blobdiff - nageru/mixer.cpp
WIP patch for async output.
[nageru] / nageru / mixer.cpp
index 80fcb64a054bf3c2c3c8047dfb28de321e4e3604..22867422858e0913eb6dcd52fcb2ddac5767b09f 100644 (file)
@@ -232,11 +232,34 @@ void JitterHistory::unregister_metrics(const vector<pair<string, string>> &label
        global_metrics.remove("input_estimated_max_jitter_seconds", labels);
 }
 
-void JitterHistory::frame_arrived(steady_clock::time_point now, int64_t frame_duration, size_t dropped_frames)
+void JitterHistory::frame_arrived(steady_clock::time_point now, int64_t frame_duration, size_t dropped_frames, bool verbose)
 {
+       if (frame_duration != last_duration) {
+               // If the frame rate changed, the input clock is also going to change,
+               // so our historical data doesn't make much sense anymore.
+               // Also, format changes typically introduce blips that are not representative
+               // of the typical frame stream. (We make the assumption that format changes
+               // don't happen all the time in regular use; if they did, we should probably
+               // rather keep the history so that we take jitter they may introduce into account.)
+               clear();
+               last_duration = frame_duration;
+               if (verbose) {
+                       fprintf(stderr, "JITTER %p: clearing due to format change\n", this);
+               }
+       }
        if (expected_timestamp > steady_clock::time_point::min()) {
                expected_timestamp += dropped_frames * nanoseconds(frame_duration * 1000000000 / TIMEBASE);
                double jitter_seconds = fabs(duration<double>(expected_timestamp - now).count());
+               if (verbose) {
+                       fprintf(stderr, "JITTER %p: expected_ts=%.6f (after adding %.1f ms for %zu dropped frames, duration=%ld), now=%.6f => %.1f ms late\n",
+                               this,
+                               duration<double>(expected_timestamp.time_since_epoch()).count(),
+                               1e3 * dropped_frames * duration<double>(nanoseconds(frame_duration * 1000000000 / TIMEBASE)).count(),
+                               dropped_frames,
+                               frame_duration,
+                               duration<double>(now.time_since_epoch()).count(),
+                               1e3 * duration<double>(now - expected_timestamp).count());
+               }
                history.push_back(orders.insert(jitter_seconds));
                if (jitter_seconds > estimate_max_jitter()) {
                        ++metric_input_underestimated_jitter_frames;
@@ -249,6 +272,12 @@ void JitterHistory::frame_arrived(steady_clock::time_point now, int64_t frame_du
                        history.pop_front();
                }
                assert(history.size() <= history_length);
+       } else if (verbose) {
+               fprintf(stderr, "JITTER %p: now=%.6f, expected=%.6f duration=%ld [initial]\n",
+                       this,
+                       duration<double>(now.time_since_epoch()).count(),
+                       duration<double>((now + nanoseconds(frame_duration * 1000000000 / TIMEBASE)).time_since_epoch()).count(),
+                       frame_duration);
        }
        expected_timestamp = now + nanoseconds(frame_duration * 1000000000 / TIMEBASE);
 }
@@ -277,18 +306,18 @@ void QueueLengthPolicy::unregister_metrics(const vector<pair<string, string>> &l
 }
 
 void QueueLengthPolicy::update_policy(steady_clock::time_point now,
-                                      steady_clock::time_point expected_next_frame,
+                                      steady_clock::time_point expected_next_input_frame,
                                       int64_t input_frame_duration,
                                       int64_t master_frame_duration,
                                       double max_input_card_jitter_seconds,
-                                      double max_master_card_jitter_seconds)
+                                      double max_master_card_jitter_seconds, bool verbose)
 {
        double input_frame_duration_seconds = input_frame_duration / double(TIMEBASE);
        double master_frame_duration_seconds = master_frame_duration / double(TIMEBASE);
 
        // Figure out when we can expect the next frame for this card, assuming
        // worst-case jitter (ie., the frame is maximally late).
-       double seconds_until_next_frame = max(duration<double>(expected_next_frame - now).count() + max_input_card_jitter_seconds, 0.0);
+       double seconds_until_next_frame = max(duration<double>(expected_next_input_frame - now).count() + max_input_card_jitter_seconds, 0.0);
 
        // How many times are the master card expected to tick in that time?
        // We assume the master clock has worst-case jitter but not any rate
@@ -308,6 +337,13 @@ void QueueLengthPolicy::update_policy(steady_clock::time_point now,
        } else {
                frames_allowed = frames_needed;
        }
+       if (verbose) {
+               fprintf(stderr, "secs_until_next_frame = %.1f ms, input jitter = %.1f ms, master jitter = %.1f ms, frames_allowed = %.3f\n",
+                       1e3 * duration<double>(expected_next_input_frame - now).count(),
+                       1e3 * max_input_card_jitter_seconds,
+                       1e3 * max_master_card_jitter_seconds,
+                       frames_allowed);
+       }
 
        safe_queue_length = max<int>(floor(frames_allowed), 0);
        metric_input_queue_safe_length_frames = safe_queue_length;
@@ -499,10 +535,6 @@ Mixer::Mixer(const QSurfaceFormat &format)
        }
 #endif
 
-       for (unsigned card_index = 0; card_index < MAX_VIDEO_CARDS; ++card_index) {
-               cards[card_index].queue_length_policy.reset(card_index);
-       }
-
        chroma_subsampler.reset(new ChromaSubsampler(resource_pool.get()));
 
        if (global_flags.ten_bit_input) {
@@ -890,10 +922,10 @@ void Mixer::set_output_card_internal(int card_index)
                card->parked_capture = move(card->capture);
                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, CardType::FAKE_CAPTURE, card->output.release(), /*is_srt_card=*/false);
-               card->queue_length_policy.reset(card_index);
+               card->jitter_history.clear();
                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);
+               card->output->start_output(desired_output_video_mode, pts_int, /*is_master_card=*/slave_to_output);
        }
        output_card_index = card_index;
        output_jitter_history.clear();
@@ -1280,7 +1312,7 @@ void Mixer::thread_func()
                        DeckLinkOutput *output = cards[output_card_index].output.get();
                        output->end_output();
                        desired_output_video_mode = output_video_mode = output->pick_video_mode(desired_output_video_mode);
-                       output->start_output(desired_output_video_mode, pts_int);
+                       output->start_output(desired_output_video_mode, pts_int, /*is_master_card=*/slave_to_output);
                }
 
                {
@@ -1293,7 +1325,7 @@ void Mixer::thread_func()
 
                bool master_card_is_output;
                unsigned master_card_index;
-               if (output_card_index != -1) {
+               if (output_card_index != -1 && slave_to_output) {
                        master_card_is_output = true;
                        master_card_index = output_card_index;
                } else {
@@ -1400,7 +1432,7 @@ void Mixer::thread_func()
 
 bool Mixer::input_card_is_master_clock(unsigned card_index, unsigned master_card_index) const
 {
-       if (output_card_index != -1) {
+       if (output_card_index != -1 && slave_to_output) {
                // The output card (ie., cards[output_card_index].output) is the master clock,
                // so no input card (ie., cards[card_index].capture) is.
                return false;
@@ -1608,7 +1640,7 @@ void Mixer::handle_hotplugged_cards()
                                fprintf(stderr, "Card %u went away, replacing with a fake card.\n", card_index);
                                FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
                                configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
-                               card->queue_length_policy.reset(card_index);
+                               card->jitter_history.clear();
                                card->capture->start_bm_capture();
                        } else {
                                // NOTE: The theme might end up forcing the card back at some later point
@@ -1618,7 +1650,7 @@ void Mixer::handle_hotplugged_cards()
                                fprintf(stderr, "Card %u went away, removing. (To keep a fake card, increase --num-cards.)\n", card_index);
                                theme->remove_card(card_index);
                                configure_card(card_index, /*capture=*/nullptr, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
-                               card->queue_length_policy.reset(card_index);
+                               card->jitter_history.clear();
                        }
                }
        }
@@ -1665,7 +1697,7 @@ void Mixer::handle_hotplugged_cards()
                        CaptureCard *card = &cards[free_card_index];
                        BMUSBCapture *capture = new BMUSBCapture(free_card_index, new_dev);
                        configure_card(free_card_index, capture, CardType::LIVE_CARD, /*output=*/nullptr, /*is_srt_card=*/false);
-                       card->queue_length_policy.reset(free_card_index);
+                       card->jitter_history.clear();
                        capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, free_card_index));
                        capture->start_bm_capture();
                }
@@ -1721,7 +1753,7 @@ void Mixer::handle_hotplugged_cards()
                        configure_card(free_card_index, capture, CardType::FFMPEG_INPUT, /*output=*/nullptr, /*is_srt_card=*/true);
                        update_srt_stats(sock, card);  // Initial zero stats.
                        card->last_srt_stream_id = stream_id;
-                       card->queue_length_policy.reset(free_card_index);
+                       card->jitter_history.clear();
                        capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, free_card_index));
                        capture->start_bm_capture();
                }
@@ -1734,7 +1766,7 @@ void Mixer::handle_hotplugged_cards()
                if (card->capture == nullptr && card->force_active) {
                        FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio);
                        configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr, /*is_srt_card=*/false);
-                       card->queue_length_policy.reset(card_index);
+                       card->jitter_history.clear();
                        card->capture->start_bm_capture();
                }
        }
@@ -1980,7 +2012,7 @@ void Mixer::quit()
        if (global_flags.srt_port >= 0) {
                // There's seemingly no other reasonable way to wake up the thread
                // (libsrt's epoll equivalent is busy-waiting).
-               int sock = srt_socket(AF_INET6, 0, 0);
+               int sock = srt_create_socket();
                if (sock != -1) {
                        sockaddr_in6 addr;
                        memset(&addr, 0, sizeof(addr));
@@ -2189,7 +2221,7 @@ void Mixer::OutputChannel::set_color_updated_callback(Mixer::color_updated_callb
 #ifdef HAVE_SRT
 void Mixer::start_srt()
 {
-       SRTSOCKET sock = srt_socket(AF_INET6, 0, 0);
+       SRTSOCKET sock = srt_create_socket();
        sockaddr_in6 addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin6_family = AF_INET6;