Fix binary STL output with subs that are too big for a single TTI block.
authorCarl Hetherington <cth@carlh.net>
Thu, 26 Mar 2020 22:03:23 +0000 (22:03 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 26 Mar 2020 22:08:49 +0000 (23:08 +0100)
Forward-ported from 82f9ab96faeef0eb684c357d76d27f3737cf1d55 in v1.4.x.

src/stl_binary_writer.cc
test/dcp_to_stl_binary_test.cc

index ba93411fe35dcdb80b169affaa4dafe2c2a79faa..456bbe17ff0c035f7967e25f4ff8d61ec65fbd46 100644 (file)
@@ -33,6 +33,7 @@
 #include <list>
 #include <cmath>
 #include <fstream>
+#include <vector>
 #include <iomanip>
 #include <set>
 
@@ -44,6 +45,7 @@ using std::setw;
 using std::setfill;
 using std::max;
 using std::cout;
+using std::vector;
 using boost::locale::conv::utf_to_utf;
 using boost::optional;
 using namespace sub;
@@ -146,27 +148,165 @@ vertical_position (sub::Line const & line)
        return vp;
 }
 
+vector<char*>
+make_tti_blocks (list<Subtitle> const& subtitles, STLBinaryTables const& tables, float frames_per_second)
+{
+       static int const tti_size = 128;
+       vector<char*> tti;
+
+       /* Buffer to build the TTI blocks in */
+       char buffer[tti_size];
+
+       BOOST_FOREACH (Subtitle const& i, subtitles) {
+
+               /* Find the top vertical position of this subtitle */
+               optional<int> top;
+               BOOST_FOREACH (Line const& j, i.lines) {
+                       int const vp = vertical_position (j);
+                       if (!top || vp < top.get ()) {
+                               top = vp;
+                       }
+               }
+
+               /* Work out the text */
+               string text;
+               bool italic = false;
+               bool underline = false;
+               optional<int> last_vp;
+
+               BOOST_FOREACH (Line const& j, i.lines) {
+
+                       /* CR/LF down to this line */
+                       int const vp = vertical_position (j);
+
+                       if (last_vp) {
+                               for (int k = last_vp.get(); k < vp; ++k) {
+                                       text += "\x8A";
+                               }
+                       }
+
+                       last_vp = vp;
+
+                       BOOST_FOREACH (Block const& k, j.blocks) {
+                               if (k.underline && !underline) {
+                                       text += "\x82";
+                                       underline = true;
+                               } else if (underline && !k.underline) {
+                                       text += "\x83";
+                                       underline = false;
+                               }
+                               if (k.italic && !italic) {
+                                       text += "\x80";
+                                       italic = true;
+                               } else if (italic && !k.italic) {
+                                       text += "\x81";
+                                       italic = false;
+                               }
+
+                               text += utf16_to_iso6937 (utf_to_utf<wchar_t> (k.text));
+                       }
+               }
+
+               /* Turn italic/underline off before the end of this subtitle */
+               if (underline) {
+                       text += "\x83";
+               }
+               if (italic) {
+                       text += "\x81";
+               }
+
+               /* Make sure there's at least one end-of-line */
+               text += "\x8F";
+
+               /* Now write this text in 112 byte chunks (TTI blocks).  Only the first TTI
+                  block's cumulative status, timecodes, vertical position, justification code
+                  and comment flag are taken into account by the reader.
+                  */
+
+               /* Set up the first part of the block */
+
+               /* XXX: these should increment, surely! */
+               /* Subtitle group number */
+               put_int_as_int (buffer + 0, 1, 1);
+               /* Subtitle number */
+               put_int_as_int (buffer + 1, 0, 2);
+               /* Cumulative status */
+               put_int_as_int (buffer + 4, tables.cumulative_status_enum_to_file (CUMULATIVE_STATUS_NOT_CUMULATIVE), 1);
+               /* Time code in */
+               put_int_as_int (buffer + 5, i.from.hours(), 1);
+               put_int_as_int (buffer + 6, i.from.minutes(), 1);
+               put_int_as_int (buffer + 7, i.from.seconds(), 1);
+               put_int_as_int (buffer + 8, i.from.frames_at(sub::Rational(frames_per_second * 1000, 1000)), 1);
+               /* Time code out */
+               put_int_as_int (buffer + 9, i.to.hours(), 1);
+               put_int_as_int (buffer + 10, i.to.minutes(), 1);
+               put_int_as_int (buffer + 11, i.to.seconds(), 1);
+               put_int_as_int (buffer + 12, i.to.frames_at(sub::Rational(frames_per_second * 1000, 1000)), 1);
+               /* Vertical position */
+               put_int_as_int (buffer + 13, top.get(), 1);
+
+               /* Justification code */
+               /* XXX: this assumes the first line has the right value */
+               switch (i.lines.front().horizontal_position.reference) {
+                       case LEFT_OF_SCREEN:
+                               put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_LEFT), 1);
+                               break;
+                       case HORIZONTAL_CENTRE_OF_SCREEN:
+                               put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_CENTRE), 1);
+                               break;
+                       case RIGHT_OF_SCREEN:
+                               put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_RIGHT), 1);
+                               break;
+               }
+
+               /* Comment flag */
+               put_int_as_int (buffer + 15, tables.comment_enum_to_file (COMMENT_NO), 1);
+
+               /* Now make as many blocks as are needed to add all the text */
+               size_t const block_size = 112;
+               size_t offset = 0;
+               int block_number = 0;
+               while (offset < text.length()) {
+                       size_t this_time = std::min(block_size, text.length() - offset);
+                       put_string (buffer + 16, text.substr(offset, this_time) + string(block_size - this_time, '\x8f'));
+                       offset += this_time;
+
+                       /* Extension block number.  Count up from 0 but use 0xff for the last one */
+                       put_int_as_int (buffer + 3, this_time < block_size ? 0xff : block_number, 1);
+                       ++block_number;
+
+                       char* finished = new char[tti_size];
+                       memcpy (finished, buffer, tti_size);
+                       tti.push_back (finished);
+               }
+       }
+
+       return tti;
+}
+
+
+
 /** @param language ISO 3-character country code for the language of the subtitles */
-void
+       void
 sub::write_stl_binary (
-       list<Subtitle> subtitles,
-       float frames_per_second,
-       Language language,
-       string original_programme_title,
-       string original_episode_title,
-       string translated_programme_title,
-       string translated_episode_title,
-       string translator_name,
-       string translator_contact_details,
-       string creation_date,
-       string revision_date,
-       int revision_number,
-       string country_of_origin,
-       string publisher,
-       string editor_name,
-       string editor_contact_details,
-       boost::filesystem::path file_name
-       )
+               list<Subtitle> subtitles,
+               float frames_per_second,
+               Language language,
+               string original_programme_title,
+               string original_episode_title,
+               string translated_programme_title,
+               string translated_episode_title,
+               string translator_name,
+               string translator_contact_details,
+               string creation_date,
+               string revision_date,
+               int revision_number,
+               string country_of_origin,
+               string publisher,
+               string editor_name,
+               string editor_contact_details,
+               boost::filesystem::path file_name
+               )
 {
        SUB_ASSERT (original_programme_title.size() <= 32);
        SUB_ASSERT (original_episode_title.size() <= 32);
@@ -182,9 +322,8 @@ sub::write_stl_binary (
        SUB_ASSERT (editor_name.size() <= 32);
        SUB_ASSERT (editor_contact_details.size() <= 32);
 
-       char* buffer = new char[1024];
+       char buffer[1024];
        memset (buffer, 0, 1024);
-       ofstream output (file_name.string().c_str ());
        STLBinaryTables tables;
 
        /* Find the longest subtitle in characters */
@@ -201,6 +340,8 @@ sub::write_stl_binary (
                }
        }
 
+       vector<char*> tti_blocks = make_tti_blocks (subtitles, tables, frames_per_second);
+
        /* Code page: 850 */
        put_string (buffer + 0, "850");
        /* Disk format code */
@@ -222,7 +363,7 @@ sub::write_stl_binary (
        put_string (buffer + 230, revision_date);
        put_int_as_string (buffer + 236, revision_number, 2);
        /* TTI blocks */
-       put_int_as_string (buffer + 238, subtitles.size(), 5);
+       put_int_as_string (buffer + 238, tti_blocks.size(), 5);
        /* Total number of subtitles */
        put_int_as_string (buffer + 243, subtitles.size(), 5);
        /* Total number of subtitle groups */
@@ -246,122 +387,10 @@ sub::write_stl_binary (
        put_string (buffer + 309, 32, editor_name);
        put_string (buffer + 341, 32, editor_contact_details);
 
+       ofstream output (file_name.string().c_str());
        output.write (buffer, 1024);
-
-       BOOST_FOREACH (Subtitle const& i, subtitles) {
-
-               /* Find the top vertical position of this subtitle */
-               optional<int> top;
-               BOOST_FOREACH (Line const& j, i.lines) {
-                       int const vp = vertical_position (j);
-                       if (!top || vp < top.get ()) {
-                               top = vp;
-                       }
-               }
-
-               memset (buffer, 0, 128);
-
-               /* XXX: these should increment, surely! */
-               /* Subtitle group number */
-               put_int_as_int (buffer + 0, 1, 1);
-               /* Subtitle number */
-               put_int_as_int (buffer + 1, 0, 2);
-               /* Extension block number.  Use 0xff here to indicate that it is the last TTI
-                  block in this subtitle "set", as we only ever use one.
-               */
-               put_int_as_int (buffer + 3, 255, 1);
-               /* Cumulative status */
-               put_int_as_int (buffer + 4, tables.cumulative_status_enum_to_file (CUMULATIVE_STATUS_NOT_CUMULATIVE), 1);
-               /* Time code in */
-               put_int_as_int (buffer + 5, i.from.hours(), 1);
-               put_int_as_int (buffer + 6, i.from.minutes(), 1);
-               put_int_as_int (buffer + 7, i.from.seconds(), 1);
-               put_int_as_int (buffer + 8, i.from.frames_at(sub::Rational(frames_per_second * 1000, 1000)), 1);
-               /* Time code out */
-               put_int_as_int (buffer + 9, i.to.hours(), 1);
-               put_int_as_int (buffer + 10, i.to.minutes(), 1);
-               put_int_as_int (buffer + 11, i.to.seconds(), 1);
-               put_int_as_int (buffer + 12, i.to.frames_at(sub::Rational(frames_per_second * 1000, 1000)), 1);
-               /* Vertical position */
-               put_int_as_int (buffer + 13, top.get(), 1);
-
-               /* Justification code */
-               /* XXX: this assumes the first line has the right value */
-               switch (i.lines.front().horizontal_position.reference) {
-               case LEFT_OF_SCREEN:
-                       put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_LEFT), 1);
-                       break;
-               case HORIZONTAL_CENTRE_OF_SCREEN:
-                       put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_CENTRE), 1);
-                       break;
-               case RIGHT_OF_SCREEN:
-                       put_int_as_int (buffer + 14, tables.justification_enum_to_file (JUSTIFICATION_RIGHT), 1);
-                       break;
-               }
-
-               /* Comment flag */
-               put_int_as_int (buffer + 15, tables.comment_enum_to_file (COMMENT_NO), 1);
-
-               /* Text */
-               string text;
-               bool italic = false;
-               bool underline = false;
-               optional<int> last_vp;
-
-               BOOST_FOREACH (Line const& j, i.lines) {
-
-                       /* CR/LF down to this line */
-                       int const vp = vertical_position (j);
-
-                       if (last_vp) {
-                               for (int k = last_vp.get(); k < vp; ++k) {
-                                       text += "\x8A";
-                               }
-                       }
-
-                       last_vp = vp;
-
-                       BOOST_FOREACH (Block const& k, j.blocks) {
-                               if (k.underline && !underline) {
-                                       text += "\x82";
-                                       underline = true;
-                               } else if (underline && !k.underline) {
-                                       text += "\x83";
-                                       underline = false;
-                               }
-                               if (k.italic && !italic) {
-                                       text += "\x80";
-                                       italic = true;
-                               } else if (italic && !k.italic) {
-                                       text += "\x81";
-                                       italic = false;
-                               }
-
-                               text += utf16_to_iso6937 (utf_to_utf<wchar_t> (k.text));
-                       }
-               }
-
-               /* Turn italic/underline off before the end of this subtitle */
-
-               if (underline) {
-                       text += "\x83";
-               }
-
-               if (italic) {
-                       text += "\x81";
-               }
-
-               if (text.length() > 111) {
-                       text = text.substr (111);
-               }
-
-               while (text.length() < 112) {
-                       text += "\x8F";
-               }
-
-               put_string (buffer + 16, text);
-               output.write (buffer, 128);
+       BOOST_FOREACH (char* i, tti_blocks) {
+               output.write (i, 128);
+               delete[] i;
        }
-
-       delete[] buffer;
 }
index ade17f8aaf3e62a0b122e1dc40266537db9023ed..58e024bde4d73a23200c387f5614261b5b6b6408 100644 (file)
@@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE (dcp_to_stl_binary_test8)
                "GBR",
                "",
                "", "",
-               "build/test/test3.stl"
+               "build/test/91a30f25-b415-4ffe-9623-bdae43a381d3_sub.stl"
                );
 
        check_file (