]> git.sesse.net Git - nageru/commitdiff
Fix an issue where we could discard a CEF frame in a situation where no new one would...
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 18 Mar 2018 22:26:00 +0000 (23:26 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 18 Mar 2018 22:26:00 +0000 (23:26 +0100)
cef_capture.cpp
cef_capture.h
mixer.cpp
mixer.h

index 853c6b4916a6b1ddcfb4aa4b715f574f51e7cb40..f692c857f0201b1800a7cd913fdb40af3b701fbb 100644 (file)
@@ -97,6 +97,19 @@ void CEFCapture::resize(unsigned width, unsigned height)
        this->height = height;
 }
 
+void CEFCapture::request_new_frame()
+{
+       // By adding a delay, we make sure we don't get a new frame
+       // delivered immediately (we probably already are on the UI thread),
+       // where we couldn't really deal with it.
+       post_to_cef_ui_thread([this] {
+               lock_guard<recursive_mutex> lock(browser_mutex);
+               if (browser != nullptr) {  // Could happen if we are shutting down.
+                       browser->GetHost()->Invalidate(PET_VIEW);
+               }
+       }, 16);
+}
+
 void CEFCapture::OnPaint(const void *buffer, int width, int height)
 {
        steady_clock::time_point timestamp = steady_clock::now();
@@ -116,16 +129,7 @@ void CEFCapture::OnPaint(const void *buffer, int width, int height)
                // (CEF only sends OnPaint when there are actual changes,
                // so we need to do this explicitly, or we could be stuck on an
                // old frame forever if the image doesn't change.)
-               //
-               // By adding a delay, we make sure we don't get a new frame
-               // delivered immediately (we probably already are on the UI thread),
-               // where we couldn't really deal with it.
-               post_to_cef_ui_thread([this] {
-                       lock_guard<recursive_mutex> lock(browser_mutex);
-                       if (browser != nullptr) {  // Could happen if we are shutting down.
-                               browser->GetHost()->Invalidate(PET_VIEW);
-                       }
-               }, 16);
+               request_new_frame();
                ++timecode;
        } else {
                assert(video_frame.size >= unsigned(width * height * 4));
index 0d9bd525b03d6bb330ea614499cbb52fdf74f3e2..30cc4879593eaece48df9e315f755d51c38de3bf 100644 (file)
@@ -86,6 +86,7 @@ public:
        void set_max_fps(int max_fps);
        void execute_javascript_async(const std::string &js);
        void resize(unsigned width, unsigned height);
+       void request_new_frame();
 
        // Callbacks from NageruCEFClient.
        void OnPaint(const void *buffer, int width, int height);
index e1d9ca9c49466a18c64fbd08c9e71069a3473dc6..d0925167e663cd522dcf6e1349cbbd341ce6d023 100644 (file)
--- a/mixer.cpp
+++ b/mixer.cpp
@@ -523,6 +523,8 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardT
        }
        card->capture.reset(capture);
        card->is_fake_capture = (card_type == CardType::FAKE_CAPTURE);
+       card->is_cef_capture = (card_type == CardType::CEF_INPUT);
+       card->may_have_dropped_last_frame = false;
        card->type = card_type;
        if (card->output.get() != output) {
                card->output.reset(output);
@@ -940,6 +942,7 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode,
                        new_frame.received_timestamp = video_frame.received_timestamp;  // Ignore the audio timestamp.
                        card->new_frames.push_back(move(new_frame));
                        card->jitter_history.frame_arrived(video_frame.received_timestamp, frame_length, dropped_frames);
+                       card->may_have_dropped_last_frame = false;
                }
                card->new_frames_changed.notify_all();
        }
@@ -1116,6 +1119,10 @@ void Mixer::trim_queue(CaptureCard *card, size_t safe_queue_length)
                card->new_frames_changed.notify_all();
                --queue_length;
                ++dropped_frames;
+
+               if (queue_length == 0 && card->is_cef_capture) {
+                       card->may_have_dropped_last_frame = true;
+               }
        }
 
        card->metric_input_dropped_frames_jitter += dropped_frames;
@@ -1180,6 +1187,14 @@ start:
                CaptureCard *card = &cards[card_index];
                if (card->new_frames.empty()) {  // Starvation.
                        ++card->metric_input_duped_frames;
+                       if (card->is_cef_capture && card->may_have_dropped_last_frame) {
+                               // Unlike other sources, CEF is not guaranteed to send us a steady
+                               // stream of frames, so we'll have to ask it to repaint the frame
+                               // we dropped. (may_have_dropped_last_frame is set whenever we
+                               // trim the queue completely away, and cleared when we actually
+                               // get a new frame.)
+                               ((CEFCapture *)card->capture.get())->request_new_frame();
+                       }
                } else {
                        new_frames[card_index] = move(card->new_frames.front());
                        has_new_frame[card_index] = true;
diff --git a/mixer.h b/mixer.h
index effc2a3d0f4217c6435509e71271694d592ae7de..289ce45c9b5542da424942b4205ec825605d5848 100644 (file)
--- a/mixer.h
+++ b/mixer.h
@@ -487,6 +487,15 @@ private:
                CardType type;
                std::unique_ptr<DeckLinkOutput> output;
 
+               // CEF only delivers frames when it actually has a change.
+               // If we trim the queue for latency reasons, we could thus
+               // end up in a situation trimming a frame that was meant to
+               // be displayed for a long time, which is really suboptimal.
+               // Thus, if we drop the last frame we have, may_have_dropped_last_frame
+               // is set to true, and the next starvation event will trigger
+               // us requestin a CEF repaint.
+               bool is_cef_capture, may_have_dropped_last_frame = false;
+
                // If this card is used for output (ie., output_card_index points to it),
                // it cannot simultaneously be uesd for capture, so <capture> gets replaced
                // by a FakeCapture. However, since reconstructing the real capture object