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());
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
+ )
+ );
+ }
+ }
}
}
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 (
}
}
+ bool first_reel = true;
for (auto reel: cpl->reels()) {
stage ("Checking reel", optional<boost::filesystem::path>());
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;
}
}
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 "";
using std::string;
using std::vector;
using std::make_pair;
+using std::make_shared;
using boost::optional;
using std::shared_ptr;
}
+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");
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");
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);
}
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");
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);
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);
}
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);
}
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);
}
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());
}