Split subtitles at reel boundaries (#1918).
authorCarl Hetherington <cth@carlh.net>
Tue, 16 Mar 2021 13:24:48 +0000 (14:24 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 16 Mar 2021 13:37:42 +0000 (14:37 +0100)
cscript
src/lib/reel_writer.cc
src/lib/writer.cc
src/lib/writer.h
test/reels_test.cc

diff --git a/cscript b/cscript
index 6e9f4da5f53805c66e560ba143d66d59f3ba749b..8e9237571318ab560ed7707fdeb03eb263998851 100644 (file)
--- a/cscript
+++ b/cscript
@@ -370,8 +370,8 @@ def dependencies(target, options):
         # Use distro-provided FFmpeg on Arch
         deps = []
 
-    deps.append(('libdcp', '8344c1c'))
-    deps.append(('libsub', '8990c30'))
+    deps.append(('libdcp', '2c1faeb'))
+    deps.append(('libsub', '33c9a57'))
     deps.append(('leqm-nrt', '131f971'))
     deps.append(('rtaudio', 'f619b76'))
     # We get our OpenSSL libraries from the environment, but we
index ad52a1ca186e8d229655f604b53c1506ef912d85..1847073737605b3611eb76f2ab4e16f93ac90c32 100644 (file)
@@ -868,11 +868,12 @@ ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track,
                DCPOMATIC_ASSERT (false);
        }
 
-       auto const vfr = film()->video_frame_rate();
+       /* timecode rate for subtitles we emit; we might as well stick to ms accuracy here, I think */
+       auto const tcr = 1000;
 
        for (auto i: subs.string) {
-               i.set_in  (dcp::Time(period.from.seconds() - _period.from.seconds(), vfr));
-               i.set_out (dcp::Time(period.to.seconds() - _period.from.seconds(), vfr));
+               i.set_in  (dcp::Time(period.from.seconds() - _period.from.seconds(), tcr));
+               i.set_out (dcp::Time(period.to.seconds() - _period.from.seconds(), tcr));
                asset->add (make_shared<dcp::SubtitleString>(i));
        }
 
@@ -880,8 +881,8 @@ ReelWriter::write (PlayerText subs, TextType type, optional<DCPTextTrack> track,
                asset->add (
                        make_shared<dcp::SubtitleImage>(
                                i.image->as_png(),
-                               dcp::Time(period.from.seconds() - _period.from.seconds(), vfr),
-                               dcp::Time(period.to.seconds() - _period.from.seconds(), vfr),
+                               dcp::Time(period.from.seconds() - _period.from.seconds(), tcr),
+                               dcp::Time(period.to.seconds() - _period.from.seconds(), tcr),
                                i.rectangle.x, dcp::HAlign::LEFT, i.rectangle.y, dcp::VAlign::TOP,
                                dcp::Time(), dcp::Time()
                                )
index ad588f0a62aa0ff0709168c89c304666ec0867fe..4386b8e26264dea29a1e4cc41dce74212e915465 100644 (file)
@@ -545,6 +545,7 @@ Writer::finish (boost::filesystem::path output_dcp)
        LOG_GENERAL_NC ("Finishing ReelWriters");
 
        for (auto& i: _reels) {
+               write_hanging_text (i);
                i.finish (output_dcp);
        }
 
@@ -798,6 +799,23 @@ Writer::write (PlayerText text, TextType type, optional<DCPTextTrack> track, DCP
        while ((*reel)->period().to <= period.from) {
                ++(*reel);
                DCPOMATIC_ASSERT (*reel != _reels.end());
+               write_hanging_text (**reel);
+       }
+
+       if (period.to > (*reel)->period().to) {
+               /* This text goes off the end of the reel.  Store parts of it that should go into
+                * other reels.
+                */
+               for (auto i = std::next(*reel); i != _reels.end(); ++i) {
+                       auto overlap = i->period().overlap(period);
+                       if (overlap) {
+                               _hanging_texts.push_back (HangingText{text, type, track, *overlap});
+                       }
+               }
+               /* Back off from the reel boundary by a couple of frames to avoid tripping checks
+                * for subtitles being too close together.
+                */
+               period.to = (*reel)->period().to - DCPTime::from_frames(2, film()->video_frame_rate());
        }
 
        (*reel)->write (text, type, track, period);
@@ -907,3 +925,17 @@ Writer::calculate_referenced_digests (boost::function<void (float)> set_progress
        }
 }
 
+
+void
+Writer::write_hanging_text (ReelWriter& reel)
+{
+       vector<HangingText> new_hanging_texts;
+       for (auto i: _hanging_texts) {
+               if (i.period.from == reel.period().from) {
+                       reel.write (i.text, i.type, i.track, i.period);
+               } else {
+                       new_hanging_texts.push_back (i);
+               }
+       }
+       _hanging_texts = new_hanging_texts;
+}
index 14d4b7faa323b7483e3eec29e70d527a93f55e1b..aab7d5fc78da6e1d5c4211093c34c04b2781d2ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -129,6 +129,7 @@ private:
        void set_digest_progress (Job* job, float progress);
        void write_cover_sheet (boost::filesystem::path output_dcp);
        void calculate_referenced_digests (boost::function<void (float)> set_progress);
+       void write_hanging_text (ReelWriter& reel);
 
        std::weak_ptr<Job> _job;
        std::vector<ReelWriter> _reels;
@@ -204,4 +205,13 @@ private:
        bool _have_subtitles = false;
        /** all closed caption tracks that we have on any reel */
        std::set<DCPTextTrack> _have_closed_captions;
+
+       struct HangingText {
+               PlayerText text;
+               TextType type;
+               boost::optional<DCPTextTrack> track;
+               dcpomatic::DCPTimePeriod period;
+       };
+
+       std::vector<HangingText> _hanging_texts;
 };
index 47ead7e125ecfba620d72c5bad40fe6b7389d2f7..a37e14067c8eff7612f37f7f6d62409198cb3f69 100644 (file)
@@ -390,7 +390,8 @@ BOOST_AUTO_TEST_CASE (reels_test10)
                {
                        dcp::VerificationNote::Code::EXTERNAL_ASSET,
                        dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE,
-                       dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
+                       dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME,
+                       dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION,
                });
 }