Fix mis-handling of reels with Atmos content (#2901). v2.17.26
authorCarl Hetherington <cth@carlh.net>
Fri, 29 Nov 2024 20:04:45 +0000 (21:04 +0100)
committerCarl Hetherington <cth@carlh.net>
Sat, 30 Nov 2024 21:18:51 +0000 (22:18 +0100)
We must have reel boundaries at least on Atmos content boundaries as I
don't know for sure how to insert silence into an Atmos stream.

src/lib/film.cc
src/lib/film.h
test/reels_test.cc

index 5b845d8b497be982e71de4359b6aaa3d505906d6..a4734d4c3711298527e7bdd392908c6ff8d95cd6 100644 (file)
@@ -1610,6 +1610,51 @@ Film::playlist_change (ChangeType type)
        set_dirty (true);
 }
 
+
+void
+Film::check_reel_boundaries_for_atmos()
+{
+       /* Find Atmos boundaries */
+       std::set<dcpomatic::DCPTime> atmos_boundaries;
+       for (auto i: content()) {
+               if (i->atmos) {
+                       atmos_boundaries.insert(i->position());
+                       atmos_boundaries.insert(i->end(shared_from_this()));
+               }
+       }
+
+       /* Find reel boundaries */
+       vector<dcpomatic::DCPTime> reel_boundaries;
+       bool first = true;
+       for (auto period: reels()) {
+               if (first) {
+                       reel_boundaries.push_back(period.from);
+                       first = false;
+               }
+               reel_boundaries.push_back(period.to);
+       }
+
+       /* There must be a reel boundary at all the Atmos boundaries */
+       auto remake_boundaries = std::any_of(atmos_boundaries.begin(), atmos_boundaries.end(), [&reel_boundaries](dcpomatic::DCPTime time) {
+               return std::find(reel_boundaries.begin(), reel_boundaries.end(), time) == reel_boundaries.end();
+       });
+
+       if (remake_boundaries) {
+               vector<dcpomatic::DCPTime> required_boundaries;
+               std::copy_if(atmos_boundaries.begin(), atmos_boundaries.end(), std::back_inserter(required_boundaries), [this](dcpomatic::DCPTime time) {
+                       return time.get() != 0 && time != length();
+               });
+               if (!required_boundaries.empty()) {
+                       set_reel_type(ReelType::CUSTOM);
+                       set_custom_reel_boundaries(required_boundaries);
+               } else {
+                       set_reel_type(ReelType::SINGLE);
+               }
+               Message(variant::insert_dcpomatic("%1 had to change your reel settings to accomodate the Atmos content"));
+       }
+}
+
+
 /** Check for (and if necessary fix) impossible settings combinations, like
  *  video set to being referenced when it can't be.
  */
index 815c6ae745ab96013c533b584eff34699507c984..8718e8f7df9ac9f002127ba032217220434cc5e6 100644 (file)
@@ -502,6 +502,7 @@ private:
        void maybe_set_container_and_resolution ();
        void set_dirty (bool dirty);
        void write_ui_state() const;
+       void check_reel_boundaries_for_atmos();
 
        /** Log to write to */
        std::shared_ptr<Log> _log;
index 7ba01b8a9e9adbff7b00b9cb90f5113e3db62b9c..5feff84f2d27af899e1f9ced860bb3e3da273e3f 100644 (file)
 #include "lib/make_dcp.h"
 #include "lib/ratio.h"
 #include "lib/string_text_file_content.h"
+#include "lib/variant.h"
 #include "lib/video_content.h"
 #include "test.h"
 #include <dcp/cpl.h>
 #include <dcp/reel.h>
+#include <dcp/reel_atmos_asset.h>
 #include <dcp/reel_picture_asset.h>
 #include <dcp/reel_sound_asset.h>
 #include <boost/test/unit_test.hpp>
@@ -647,3 +649,32 @@ BOOST_AUTO_TEST_CASE (repeated_dcp_into_reels)
                BOOST_REQUIRE_EQUAL(cpl->reels()[i]->main_sound()->id(), sound->id());
        }
 }
+
+
+/** Check that reel lengths are adapted to cope with Atmos content, as I don't know how to fill gaps with Atmos silence */
+BOOST_AUTO_TEST_CASE(reel_assets_same_length_with_atmos)
+{
+       auto atmos = content_factory("test/data/atmos_0.mxf")[0];
+       auto film = new_test_film("reel_assets_same_length_with_atmos", { atmos });
+
+       vector<string> messages;
+       film->Message.connect([&messages](string message) {
+               messages.push_back(message);
+       });
+
+       auto picture = content_factory("test/data/flat_red.png")[0];
+       film->examine_and_add_content(picture);
+       wait_for_jobs();
+       picture->video->set_length(480);
+
+       BOOST_REQUIRE_EQUAL(messages.size(), 1U);
+       BOOST_CHECK_EQUAL(messages[0], variant::insert_dcpomatic("%1 had to change your reel settings to accomodate the Atmos content"));
+
+       auto const reels = film->reels();
+       BOOST_CHECK_EQUAL(reels.size(), 2U);
+       BOOST_CHECK(reels[0] == dcpomatic::DCPTimePeriod({}, dcpomatic::DCPTime::from_frames(240, 24)));
+       BOOST_CHECK(reels[1] == dcpomatic::DCPTimePeriod(dcpomatic::DCPTime::from_frames(240, 24), dcpomatic::DCPTime::from_frames(480, 24)));
+
+       make_and_verify_dcp(film, { dcp::VerificationNote::Code::MISSING_CPL_METADATA });
+}
+