summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-03-05 19:58:24 +0100
committerCarl Hetherington <cth@carlh.net>2025-03-06 22:57:34 +0100
commit3b908673629f4a475abcf1aa56f3b03c60fca13a (patch)
tree8dff258d0cbec9f69d4f1ebf9c6f75e8633df656
parent8f65aa1a9a63a05ff809a35b0708607d6f77dc7c (diff)
Fix black frames when raising frame rate by a lot (#2993).
-rw-r--r--src/lib/player.cc17
-rw-r--r--test/player_test.cc26
2 files changed, 39 insertions, 4 deletions
diff --git a/src/lib/player.cc b/src/lib/player.cc
index fd99e7732..4c988c804 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -997,7 +997,16 @@ Player::emit_video_until(DCPTime time)
emit_video(to_do.first, to_do.second);
};
- auto const age_threshold = one_video_frame() * 2;
+ auto film = _film.lock();
+ DCPOMATIC_ASSERT(film);
+
+ auto const age_threshold = [this, film](pair<shared_ptr<PlayerVideo>, dcpomatic::DCPTime> video) {
+ if (auto content = video.first->content().lock()) {
+ return one_video_frame() * (std::ceil(film->video_frame_rate() / content->video_frame_rate().get_value_or(24)) + 1);
+ } else {
+ return one_video_frame() * 2;
+ }
+ };
while (_next_video_time.get_value_or({}) < time) {
auto left = _last_video[Eyes::LEFT];
@@ -1010,12 +1019,12 @@ Player::emit_video_until(DCPTime time)
left.first &&
right.first &&
(!both.first || (left.second >= both.second && right.second >= both.second)) &&
- (left.second - next) < age_threshold &&
- (right.second - next) < age_threshold
+ (left.second - next) < age_threshold(left) &&
+ (right.second - next) < age_threshold(right)
) {
frame(left.first, next);
frame(right.first, next);
- } else if (both.first && (both.second - next) < age_threshold) {
+ } else if (both.first && (both.second - next) < age_threshold(both)) {
frame(both.first, next);
LOG_DEBUG_PLAYER("Content %1 selected for DCP %2 (age %3)", to_string(both.second), to_string(next), to_string(both.second - next));
} else {
diff --git a/test/player_test.cc b/test/player_test.cc
index eb49bc4df..60b365eb1 100644
--- a/test/player_test.cc
+++ b/test/player_test.cc
@@ -731,3 +731,29 @@ BOOST_AUTO_TEST_CASE(unmapped_audio_does_not_raise_buffer_error)
butler.rethrow();
}
+
+BOOST_AUTO_TEST_CASE(frames_are_copied_correctly_for_low_frame_rates)
+{
+ auto content = content_factory("test/data/flat_red.png");
+ auto film = new_test_film("frames_are_copied_correctly_for_low_frame_rates", content);
+
+ content[0]->set_video_frame_rate(film, 10);
+ film->set_video_frame_rate(30);
+
+ Player player(film, Image::Alignment::COMPACT, false);
+ Butler butler(film, player, AudioMapping(), 2, bind(PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, Image::Alignment::PADDED, true, false, Butler::Audio::ENABLED);
+
+ /* Check that only red frames come out - previously there would be some black ones mixed in */
+ for (auto i = 0; i < 24; ++i) {
+ auto frame = butler.get_video(Butler::Behaviour::BLOCKING);
+ auto image = frame.first->image([](AVPixelFormat) { return AV_PIX_FMT_RGB24; }, VideoRange::FULL, false);
+ for (int y = 0; y < image->size().height; ++y) {
+ uint8_t const* p = image->data()[0] + image->stride()[0] * y;
+ for (int x = 0; x < image->size().width; ++x) {
+ BOOST_REQUIRE_EQUAL(p[0], 255);
+ BOOST_REQUIRE_EQUAL(p[1], 0);
+ BOOST_REQUIRE_EQUAL(p[2], 0);
+ }
+ }
+ }
+}