summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2021-01-09 01:39:08 +0100
committerCarl Hetherington <cth@carlh.net>2021-01-17 20:13:22 +0100
commit3034388f30732db8d748da96ee66fcdde0626c52 (patch)
treefc8de1e5e82832a08958b78e9d656fc3aa79f9b2
parent9dcee1cb0ab00ed75c0f56bc9708a01aa358b62b (diff)
Bv2.1 7.2.4: first subtitle should be at least 4s into the DCP.
-rw-r--r--src/verify.cc31
-rw-r--r--src/verify.h2
-rw-r--r--test/verify_test.cc214
3 files changed, 179 insertions, 68 deletions
diff --git a/src/verify.cc b/src/verify.cc
index 8f04f799..6a9967e6 100644
--- a/src/verify.cc
+++ b/src/verify.cc
@@ -651,7 +651,8 @@ verify_subtitle_asset (
function<void (string, optional<boost::filesystem::path>)> stage,
boost::filesystem::path xsd_dtd_directory,
list<VerificationNote>& notes,
- State& state
+ State& state,
+ bool first_reel
)
{
stage ("Checking subtitle XML", asset->file());
@@ -715,6 +716,20 @@ verify_subtitle_asset (
VerificationNote::VERIFY_BV21_ERROR, VerificationNote::SUBTITLE_START_TIME_NON_ZERO, *asset->file())
);
}
+
+ if (first_reel) {
+ auto subs = smpte->subtitles();
+ subs.sort ([](shared_ptr<Subtitle> a, shared_ptr<Subtitle> b) {
+ return a->in() < b->in();
+ });
+ if (!subs.empty() && subs.front()->in() < dcp::Time(0, 0, 4, 0, 24)) {
+ notes.push_back(
+ VerificationNote(
+ VerificationNote::VERIFY_WARNING, VerificationNote::FIRST_TEXT_TOO_EARLY
+ )
+ );
+ }
+ }
}
}
@@ -725,10 +740,11 @@ verify_closed_caption_asset (
function<void (string, optional<boost::filesystem::path>)> stage,
boost::filesystem::path xsd_dtd_directory,
list<VerificationNote>& notes,
- State& state
+ State& state,
+ bool first_reel
)
{
- verify_subtitle_asset (asset, stage, xsd_dtd_directory, notes, state);
+ verify_subtitle_asset (asset, stage, xsd_dtd_directory, notes, state, first_reel);
if (asset->raw_xml().size() > 256 * 1024) {
notes.push_back (
@@ -796,6 +812,7 @@ dcp::verify (
}
}
+ bool first_reel = true;
for (auto reel: cpl->reels()) {
stage ("Checking reel", optional<boost::filesystem::path>());
@@ -834,16 +851,18 @@ dcp::verify (
if (reel->main_subtitle()) {
verify_main_subtitle_reel (reel->main_subtitle(), notes);
if (reel->main_subtitle()->asset_ref().resolved()) {
- verify_subtitle_asset (reel->main_subtitle()->asset(), stage, xsd_dtd_directory, notes, state);
+ verify_subtitle_asset (reel->main_subtitle()->asset(), stage, xsd_dtd_directory, notes, state, first_reel);
}
}
for (auto i: reel->closed_captions()) {
verify_closed_caption_reel (i, notes);
if (i->asset_ref().resolved()) {
- verify_closed_caption_asset (i->asset(), stage, xsd_dtd_directory, notes, state);
+ verify_closed_caption_asset (i->asset(), stage, xsd_dtd_directory, notes, state, first_reel);
}
}
+
+ first_reel = false;
}
}
@@ -927,6 +946,8 @@ dcp::note_to_string (dcp::VerificationNote note)
return String::compose("The XML for a SMPTE subtitle asset has no <StartTime> tag, which is required by Bv2.1", note.file()->filename());
case dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO:
return String::compose("The XML for a SMPTE subtitle asset has a non-zero <StartTime> tag, which is disallowed by Bv2.1", note.file()->filename());
+ case dcp::VerificationNote::FIRST_TEXT_TOO_EARLY:
+ return "The first subtitle or closed caption is less than 4 seconds from the start of the DCP.";
}
return "";
diff --git a/src/verify.h b/src/verify.h
index 2957654f..f692e867 100644
--- a/src/verify.h
+++ b/src/verify.h
@@ -116,6 +116,8 @@ public:
MISSING_SUBTITLE_START_TIME,
/** Some SMPTE subtitle XML has a non-zero <StartTime> tag [Bv2.1_7.2.3] */
SUBTITLE_START_TIME_NON_ZERO,
+ /** The first subtitle or closed caption happens before 4s into the first reel [Bv2.1_7.2.4] */
+ FIRST_TEXT_TOO_EARLY,
};
VerificationNote (Type type, Code code)
diff --git a/test/verify_test.cc b/test/verify_test.cc
index 4cde3966..863ff9f7 100644
--- a/test/verify_test.cc
+++ b/test/verify_test.cc
@@ -61,6 +61,7 @@ using std::pair;
using std::string;
using std::vector;
using std::make_pair;
+using std::make_shared;
using boost::optional;
using std::shared_ptr;
@@ -1163,6 +1164,36 @@ BOOST_AUTO_TEST_CASE (verify_picture_size)
}
+static
+void
+add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame)
+{
+ asset->add (
+ make_shared<dcp::SubtitleString>(
+ optional<string>(),
+ false,
+ false,
+ false,
+ dcp::Colour(),
+ 42,
+ 1,
+ dcp::Time(start_frame, 24, 24),
+ dcp::Time(end_frame, 24, 24),
+ 0,
+ dcp::HALIGN_CENTER,
+ 0,
+ dcp::VALIGN_CENTER,
+ dcp::DIRECTION_LTR,
+ "Hello",
+ dcp::NONE,
+ dcp::Colour(),
+ dcp::Time(),
+ dcp::Time()
+ )
+ );
+}
+
+
BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
{
boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
@@ -1170,31 +1201,7 @@ BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset());
for (int i = 0; i < 2048; ++i) {
- asset->add (
- shared_ptr<dcp::Subtitle>(
- new dcp::SubtitleString(
- optional<string>(),
- false,
- false,
- false,
- dcp::Colour(),
- 42,
- 1,
- dcp::Time(i * 24, 24, 24),
- dcp::Time(i * 24 + 20, 24, 24),
- 0,
- dcp::HALIGN_CENTER,
- 0,
- dcp::VALIGN_CENTER,
- dcp::DIRECTION_LTR,
- "Hello",
- dcp::NONE,
- dcp::Colour(),
- dcp::Time(),
- dcp::Time()
- )
- )
- );
+ add_test_subtitle (asset, i * 24, i * 24 + 20);
}
asset->set_language (dcp::LanguageTag("de-DE"));
asset->write (dir / "subs.mxf");
@@ -1210,10 +1217,12 @@ BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
vector<boost::filesystem::path> dirs;
dirs.push_back (dir);
list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
- BOOST_REQUIRE_EQUAL (notes.size(), 2U);
+ BOOST_REQUIRE_EQUAL (notes.size(), 3U);
list<dcp::VerificationNote>::const_iterator i = notes.begin();
BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
++i;
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
+ ++i;
BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES);
}
@@ -1240,31 +1249,7 @@ verify_timed_text_asset_too_large (string name)
boost::filesystem::path const dir = boost::filesystem::path("build/test") / name;
prepare_directory (dir);
shared_ptr<dcp::SMPTESubtitleAsset> asset = make_large_subtitle_asset (dir / "font.ttf");
- asset->add (
- shared_ptr<dcp::Subtitle>(
- new dcp::SubtitleString(
- optional<string>(),
- false,
- false,
- false,
- dcp::Colour(),
- 42,
- 1,
- dcp::Time(0, 24, 24),
- dcp::Time(20, 24, 24),
- 0,
- dcp::HALIGN_CENTER,
- 0,
- dcp::VALIGN_CENTER,
- dcp::DIRECTION_LTR,
- "Hello",
- dcp::NONE,
- dcp::Colour(),
- dcp::Time(),
- dcp::Time()
- )
- )
- );
+ add_test_subtitle (asset, 0, 20);
asset->set_language (dcp::LanguageTag("de-DE"));
asset->write (dir / "subs.mxf");
@@ -1280,7 +1265,8 @@ verify_timed_text_asset_too_large (string name)
vector<boost::filesystem::path> dirs;
dirs.push_back (dir);
list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
- BOOST_REQUIRE_EQUAL (notes.size(), 3U);
+ BOOST_REQUIRE_EQUAL (notes.size(), 4U);
+ dump_notes (notes);
list<dcp::VerificationNote>::const_iterator i = notes.begin();
BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES);
@@ -1289,6 +1275,8 @@ verify_timed_text_asset_too_large (string name)
BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES);
++i;
BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
+ ++i;
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
}
@@ -1339,10 +1327,13 @@ BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
vector<boost::filesystem::path> dirs;
dirs.push_back (dir);
- list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
- BOOST_REQUIRE_EQUAL (notes.size(), 1U);
- BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
- BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE);
+ auto notes = dcp::verify (dirs, &stage, &progress, xsd_test);
+ BOOST_REQUIRE_EQUAL (notes.size(), 2U);
+ auto i = notes.begin();
+ BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE);
+ ++i;
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
}
@@ -1425,10 +1416,13 @@ BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
vector<boost::filesystem::path> dirs;
dirs.push_back (dir);
- list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
- BOOST_REQUIRE_EQUAL (notes.size(), 1U);
- BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
- BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
+ auto notes = dcp::verify (dirs, &stage, &progress, xsd_test);
+ BOOST_REQUIRE_EQUAL (notes.size(), 2U);
+ auto i = notes.begin();
+ BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
+ ++i;
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
}
@@ -1473,8 +1467,102 @@ BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
vector<boost::filesystem::path> dirs;
dirs.push_back (dir);
- list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
+ auto notes = dcp::verify (dirs, &stage, &progress, xsd_test);
+ BOOST_REQUIRE_EQUAL (notes.size(), 2U);
+ auto i = notes.begin();
+ BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO);
+ ++i;
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_text_too_early)
+{
+ auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
+ prepare_directory (dir);
+ auto asset = make_shared<dcp::SMPTESubtitleAsset>();
+ asset->set_start_time (dcp::Time());
+ /* Just too early */
+ add_test_subtitle (asset, 4 * 24 - 1, 5 * 24);
+ asset->set_language (dcp::LanguageTag("de-DE"));
+ asset->write (dir / "subs.mxf");
+
+ auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
+ auto reel = make_shared<dcp::Reel>();
+ reel->add (reel_asset);
+ auto cpl = make_shared<dcp::CPL>("hello", dcp::FEATURE);
+ cpl->add (reel);
+ auto dcp = make_shared<dcp::DCP>(dir);
+ dcp->add (cpl);
+ dcp->write_xml (dcp::SMPTE);
+
+ auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
BOOST_REQUIRE_EQUAL (notes.size(), 1U);
- BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
- BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO);
+ auto i = notes.begin();
+ BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
+{
+ auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
+ prepare_directory (dir);
+ auto asset = make_shared<dcp::SMPTESubtitleAsset>();
+ asset->set_start_time (dcp::Time());
+ /* Just late enough */
+ add_test_subtitle (asset, 4 * 24, 5 * 24);
+ asset->set_language (dcp::LanguageTag("de-DE"));
+ asset->write (dir / "subs.mxf");
+
+ auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
+ auto reel = make_shared<dcp::Reel>();
+ reel->add (reel_asset);
+ auto cpl = make_shared<dcp::CPL>("hello", dcp::FEATURE);
+ cpl->add (reel);
+ auto dcp = make_shared<dcp::DCP>(dir);
+ dcp->add (cpl);
+ dcp->write_xml (dcp::SMPTE);
+
+ auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
+ dump_notes (notes);
+ BOOST_REQUIRE (notes.empty());
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
+{
+ auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
+ prepare_directory (dir);
+
+ auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
+ asset1->set_start_time (dcp::Time());
+ /* Just late enough */
+ add_test_subtitle (asset1, 4 * 24, 5 * 24);
+ asset1->set_language (dcp::LanguageTag("de-DE"));
+ asset1->write (dir / "subs.mxf");
+ auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
+ auto reel1 = make_shared<dcp::Reel>();
+ reel1->add (reel_asset1);
+
+ auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
+ asset2->set_start_time (dcp::Time());
+ /* This would be too early on first reel but should be OK on the second */
+ add_test_subtitle (asset2, 0, 4 * 24);
+ asset2->set_language (dcp::LanguageTag("de-DE"));
+ asset2->write (dir / "subs.mxf");
+ auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
+ auto reel2 = make_shared<dcp::Reel>();
+ reel2->add (reel_asset2);
+
+ auto cpl = make_shared<dcp::CPL>("hello", dcp::FEATURE);
+ cpl->add (reel1);
+ cpl->add (reel2);
+ auto dcp = make_shared<dcp::DCP>(dir);
+ dcp->add (cpl);
+ dcp->write_xml (dcp::SMPTE);
+
+ auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
+ dump_notes (notes);
+ BOOST_REQUIRE (notes.empty());
}