diff options
| author | Carl Hetherington <cth@carlh.net> | 2026-03-26 23:43:00 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2026-03-26 23:43:00 +0100 |
| commit | 1c42e6a464ad25a2230540f639fe513d2761d27d (patch) | |
| tree | b4c8b1c32111a4ddfbeb6429c5eada703edd3271 | |
| parent | 01bf601526da5d6eec21e8a54dbd82f646bfedb9 (diff) | |
Put asset/reel index context into all text reels.
| -rw-r--r-- | src/verify.cc | 103 | ||||
| -rw-r--r-- | test/verify_test.cc | 36 |
2 files changed, 75 insertions, 64 deletions
diff --git a/src/verify.cc b/src/verify.cc index 8dcd6410..f071f53e 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -878,6 +878,7 @@ verify_text_details ( Context& context, vector<shared_ptr<Reel>> reels, int edit_rate, + std::function<std::string (shared_ptr<Reel>)> asset_id, std::function<bool (shared_ptr<Reel>)> check, std::function<optional<string> (shared_ptr<Reel>)> xml, std::function<int64_t (shared_ptr<Reel>)> duration, @@ -886,19 +887,22 @@ verify_text_details ( { /* end of last subtitle (in editable units) */ optional<int64_t> last_out; - auto too_short = false; - auto too_short_bv21 = false; - auto too_close = false; - auto too_early = false; - auto reel_overlap = false; - auto empty_text = false; /* current reel start time (in editable units) */ int64_t reel_offset = 0; - optional<string> missing_load_font_id; - std::function<void (cxml::ConstNodePtr, optional<int>, optional<Time>, int, bool, bool&, vector<string>&)> parse; + struct Errors { + bool too_short = false; + bool too_short_bv21 = false; + bool too_close = false; + bool too_early = false; + bool empty_text = false; + optional<string> missing_load_font_id; + }; + + std::function<void (Errors&, cxml::ConstNodePtr, optional<int>, optional<Time>, int, bool, bool&, vector<string>&)> parse; - parse = [&parse, &last_out, &too_short, &too_short_bv21, &too_close, &too_early, &empty_text, &reel_offset, &missing_load_font_id]( + parse = [&parse, &last_out, &reel_offset]( + Errors& errors, cxml::ConstNodePtr node, optional<int> tcr, optional<Time> start_time, @@ -917,19 +921,19 @@ verify_text_details ( out -= *start_time; } if (first_reel && tcr && in < Time(0, 0, 4, 0, *tcr)) { - too_early = true; + errors.too_early = true; } auto length = out - in; if (length.as_editable_units_ceil(er) <= 0) { - too_short = true; + errors.too_short = true; } else if (length.as_editable_units_ceil(er) < 15) { - too_short_bv21 = true; + errors.too_short_bv21 = true; } if (last_out) { /* XXX: this feels dubious - is it really what Bv2.1 means? */ auto distance = reel_offset + in.as_editable_units_ceil(er) - *last_out; if (distance >= 0 && distance < 2) { - too_close = true; + errors.too_close = true; } } last_out = reel_offset + out.as_editable_units_floor(er); @@ -946,7 +950,7 @@ verify_text_details ( return false; }; if (!node_has_content(node)) { - empty_text = true; + errors.empty_text = true; } has_text = true; } else if (node->name() == "LoadFont") { @@ -958,18 +962,23 @@ verify_text_details ( } else if (node->name() == "Font") { if (auto const font_id = node->optional_string_attribute("Id")) { if (std::find_if(font_ids.begin(), font_ids.end(), [font_id](string const& id) { return id == font_id; }) == font_ids.end()) { - missing_load_font_id = font_id; + errors.missing_load_font_id = font_id; } } } for (auto i: node->node_children()) { - parse(i, tcr, start_time, er, first_reel, has_text, font_ids); + parse(errors, i, tcr, start_time, er, first_reel, has_text, font_ids); } }; for (auto i = 0U; i < reels.size(); ++i) { + + bool reel_overlap = false; + Errors errors; + context.reel_index = i; - dcp::ScopeGuard sg = [&context]() { context.reel_index = boost::none; }; + context.asset_id = asset_id(reels[i]); + dcp::ScopeGuard sg = [&context]() { context.reel_index = boost::none; context.asset_id = boost::none; }; if (!check(reels[i])) { continue; @@ -1004,7 +1013,7 @@ verify_text_details ( } bool has_text = false; vector<string> font_ids; - parse(doc, tcr, start_time, edit_rate, i == 0, has_text, font_ids); + parse(errors, doc, tcr, start_time, edit_rate, i == 0, has_text, font_ids); auto end = reel_offset + duration(reels[i]); if (last_out && *last_out > end) { reel_overlap = true; @@ -1014,38 +1023,38 @@ verify_text_details ( if (context.dcp->standard() && *context.dcp->standard() == dcp::Standard::SMPTE && has_text && font_ids.empty()) { context.add_note(dcp::VerificationNote(VerificationNote::Code::MISSING_LOAD_FONT).set_asset_id(id(reels[i]))); } - } - if (last_out && *last_out > reel_offset) { - reel_overlap = true; - } + if (last_out && *last_out > reel_offset) { + reel_overlap = true; + } - if (too_early) { - context.add_note(VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME); - } + if (reel_overlap) { + context.add_note(VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY); + } - if (too_short) { - context.add_note(VerificationNote::Code::INVALID_SUBTITLE_DURATION); - } + if (errors.too_early) { + context.add_note(VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME); + } - if (too_short_bv21) { - context.add_note(VerificationNote::Code::INVALID_SUBTITLE_DURATION_BV21); - } + if (errors.too_short) { + context.add_note(VerificationNote::Code::INVALID_SUBTITLE_DURATION); + } - if (too_close) { - context.add_note(VerificationNote::Code::INVALID_SUBTITLE_SPACING); - } + if (errors.too_short_bv21) { + context.add_note(VerificationNote::Code::INVALID_SUBTITLE_DURATION_BV21); + } - if (reel_overlap) { - context.add_note(VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY); - } + if (errors.too_close) { + context.add_note(VerificationNote::Code::INVALID_SUBTITLE_SPACING); + } - if (empty_text) { - context.add_note(VerificationNote::Code::EMPTY_TEXT); - } + if (errors.empty_text) { + context.add_note(VerificationNote::Code::EMPTY_TEXT); + } - if (missing_load_font_id) { - context.add_note(dcp::VerificationNote(VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_load_font_id(*missing_load_font_id)); + if (errors.missing_load_font_id) { + context.add_note(dcp::VerificationNote(VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_load_font_id(*errors.missing_load_font_id)); + } } } @@ -1268,10 +1277,11 @@ verify_text_details(Context& context, vector<shared_ptr<Reel>> reels) } if (reels[0]->main_subtitle() && reels[0]->main_subtitle()->asset_ref().resolved()) { - context.asset_id = reels[0]->main_subtitle()->asset()->id(); - dcp::ScopeGuard sg = [&context]() { context.asset_id = boost::none; }; verify_text_details(context, reels, reels[0]->main_subtitle()->edit_rate().numerator, [](shared_ptr<Reel> reel) { + return reel->main_subtitle() ? reel->main_subtitle()->id() : string{}; + }, + [](shared_ptr<Reel> reel) { return static_cast<bool>(reel->main_subtitle()); }, [](shared_ptr<Reel> reel) { @@ -1287,10 +1297,11 @@ verify_text_details(Context& context, vector<shared_ptr<Reel>> reels) } for (auto i = 0U; i < reels[0]->closed_captions().size(); ++i) { - context.asset_id = reels[0]->closed_captions()[i]->asset()->id(); - dcp::ScopeGuard sg = [&context]() { context.asset_id = boost::none; }; verify_text_details(context, reels, reels[0]->closed_captions()[i]->edit_rate().numerator, [i](shared_ptr<Reel> reel) { + return reel->closed_captions()[i]->id(); + }, + [i](shared_ptr<Reel> reel) { return i < reel->closed_captions().size(); }, [i](shared_ptr<Reel> reel) { diff --git a/test/verify_test.cc b/test/verify_test.cc index d95aeac7..59f2fae1 100644 --- a/test/verify_test.cc +++ b/test/verify_test.cc @@ -1481,8 +1481,8 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles) note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), note(VC::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), - VN(VC::EMPTY_TEXT).set_cpl_id(cpl->id()).set_asset_id(asset->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(asset->id()), + VN(VC::EMPTY_TEXT).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()), VN(VC::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"}).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), @@ -1544,7 +1544,7 @@ BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), VN(VC::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get())).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), { VC::INVALID_STANDARD }, - VN(VC::EMPTY_TEXT).set_cpl_id(cpl->id()).set_asset_id(subs_id), + VN(VC::EMPTY_TEXT).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), VN(VC::MISSING_FONT, string{"font0"}).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), }); } @@ -2153,7 +2153,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes) note(VC::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl), VN(VC::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, string("419371"), canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(asset->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -2209,7 +2209,7 @@ verify_timed_text_asset_too_large (string name) canonical(dir / "subs.mxf") ).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(asset->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -2284,7 +2284,7 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language) note(VC::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl).set_reel_index(0).set_asset_id(picture_id), note(VC::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl).set_reel_index(0).set_asset_id(picture_id), VN(VC::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(subs_id), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id) }); } @@ -2481,7 +2481,7 @@ BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time) note(VC::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl), note(VC::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl).set_reel_index(0).set_asset_id(picture_id), VN(VC::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(subs->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs->id()), }); } @@ -2548,7 +2548,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time) ).set_cpl_id(cpl->id()), note(VC::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl).set_reel_index(0).set_asset_id(picture_id), VN(VC::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(subs->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs->id()), }); } @@ -2653,7 +2653,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time) note(VC::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(subs_id), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); @@ -2768,7 +2768,7 @@ BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing) note(VC::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), - VN(VC::INVALID_SUBTITLE_SPACING).set_cpl_id(cpl->id()).set_asset_id(subs_id), + VN(VC::INVALID_SUBTITLE_SPACING).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -2823,7 +2823,7 @@ BOOST_AUTO_TEST_CASE(verify_invalid_subtitle_duration) note(VC::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), - VN(VC::INVALID_SUBTITLE_DURATION).set_cpl_id(cpl->id()).set_asset_id(subs_id), + VN(VC::INVALID_SUBTITLE_DURATION).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -2849,7 +2849,7 @@ BOOST_AUTO_TEST_CASE(verify_invalid_subtitle_duration_bv21) note(VC::MATCHING_PKL_ANNOTATION_TEXT_WITH_CPL, cpl), note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), - VN(VC::INVALID_SUBTITLE_DURATION_BV21).set_cpl_id(cpl->id()).set_asset_id(subs_id), + VN(VC::INVALID_SUBTITLE_DURATION_BV21).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_id), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -2905,8 +2905,8 @@ BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary) note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), VN(VC::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get())).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(asset->id()), - VN(VC::SUBTITLE_OVERLAPS_REEL_BOUNDARY).set_cpl_id(cpl->id()).set_asset_id(asset->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), + VN(VC::SUBTITLE_OVERLAPS_REEL_BOUNDARY).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -4960,7 +4960,7 @@ BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id) note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), VN(VC::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf)).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), VN(VC::MISMATCHED_TIMED_TEXT_RESOURCE_ID).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(subs_asset->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()) }); } @@ -5035,7 +5035,7 @@ BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id) note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), VN(VC::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf)).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), VN(VC::INCORRECT_TIMED_TEXT_ASSET_ID).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id(subs_asset->id()), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), VN(VC::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id()), VN(VC::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"}).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(subs_asset->id()), }); @@ -5691,7 +5691,7 @@ BOOST_AUTO_TEST_CASE(verify_too_many_subtitle_namespaces) note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), VN(VC::MISSING_FFEC_IN_FEATURE).set_cpl_id(cpl->id()), VN(VC::MISSING_FFMC_IN_FEATURE).set_cpl_id(cpl->id()), - VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_asset_id("315de731-1173-484c-9a35-bdacf5a9d99d"), + VN(VC::INVALID_SUBTITLE_FIRST_TEXT_TIME).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id("315de731-1173-484c-9a35-bdacf5a9d99d"), VN(VC::MISSING_SUBTITLE_LANGUAGE, canonical(find_file(dir, "sub_"))).set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id("315de731-1173-484c-9a35-bdacf5a9d99d"), VN(VC::MISSING_CPL_METADATA, canonical(find_file(dir, "cpl_"))).set_cpl_id(cpl->id()), VN( @@ -5728,7 +5728,7 @@ BOOST_AUTO_TEST_CASE(verify_missing_load_font_for_font) note(VC::VALID_CONTENT_KIND, string{"trailer"}, cpl), note(VC::VALID_CONTENT_VERSION_LABEL_TEXT, cpl->content_version()->label_text, cpl), { VC::INVALID_STANDARD }, - VN(VC::MISSING_LOAD_FONT_FOR_FONT).set_load_font_id("theFontId").set_cpl_id(cpl->id()).set_asset_id(asset->id()) + VN(VC::MISSING_LOAD_FONT_FOR_FONT).set_load_font_id("theFontId").set_cpl_id(cpl->id()).set_reel_index(0).set_asset_id(asset->id()) }); } |
