X-Git-Url: https://main.carlh.net/gitweb/?p=libdcp.git;a=blobdiff_plain;f=src%2Fverify.cc;h=e45c381e4f725e22f51c9969848eb682672b2e78;hp=0fcb88353979764a5d5a2d4749041e839f891237;hb=2f2643b6ddc36d6efcf4d41913ec4f711750e9c4;hpb=961e8d6b0215f61ce0e39bedcf7d5b216336eae6 diff --git a/src/verify.cc b/src/verify.cc index 0fcb8835..e45c381e 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -31,6 +31,12 @@ files in the program, then also delete it here. */ + +/** @file src/verify.cc + * @brief dcp::verify() method and associated code + */ + + #include "verify.h" #include "dcp.h" #include "cpl.h" @@ -67,12 +73,12 @@ #include #include #include -#include #include #include #include #include + using std::list; using std::vector; using std::string; @@ -86,9 +92,11 @@ using boost::optional; using boost::function; using std::dynamic_pointer_cast; + using namespace dcp; using namespace xercesc; + static string xml_ch_to_string (XMLCh const * a) @@ -99,6 +107,7 @@ xml_ch_to_string (XMLCh const * a) return o; } + class XMLValidationError { public: @@ -184,7 +193,8 @@ private: list _errors; }; -class StringToXMLCh : public boost::noncopyable + +class StringToXMLCh { public: StringToXMLCh (string a) @@ -192,6 +202,9 @@ public: _buffer = XMLString::transcode(a.c_str()); } + StringToXMLCh (StringToXMLCh const&) = delete; + StringToXMLCh& operator= (StringToXMLCh const&) = delete; + ~StringToXMLCh () { XMLString::release (&_buffer); @@ -205,6 +218,7 @@ private: XMLCh* _buffer; }; + class LocalFileResolver : public EntityResolver { public: @@ -343,40 +357,38 @@ validate_xml (T xml, boost::filesystem::path xsd_dtd_directory, vector dcp, shared_ptr reel_mxf, function progress) +verify_asset (shared_ptr dcp, shared_ptr reel_file_asset, function progress) { - auto const actual_hash = reel_mxf->asset_ref()->hash(progress); + auto const actual_hash = reel_file_asset->asset_ref()->hash(progress); auto pkls = dcp->pkls(); /* We've read this DCP in so it must have at least one PKL */ DCP_ASSERT (!pkls.empty()); - auto asset = reel_mxf->asset_ref().asset(); + auto asset = reel_file_asset->asset_ref().asset(); optional pkl_hash; for (auto i: pkls) { - pkl_hash = i->hash (reel_mxf->asset_ref()->id()); + pkl_hash = i->hash (reel_file_asset->asset_ref()->id()); if (pkl_hash) { break; } @@ -384,16 +396,16 @@ verify_asset (shared_ptr dcp, shared_ptr reel_mxf, fun DCP_ASSERT (pkl_hash); - auto cpl_hash = reel_mxf->hash(); + auto cpl_hash = reel_file_asset->hash(); if (cpl_hash && *cpl_hash != *pkl_hash) { - return VERIFY_ASSET_RESULT_CPL_PKL_DIFFER; + return VerifyAssetResult::CPL_PKL_DIFFER; } if (actual_hash != *pkl_hash) { - return VERIFY_ASSET_RESULT_BAD; + return VerifyAssetResult::BAD; } - return VERIFY_ASSET_RESULT_GOOD; + return VerifyAssetResult::GOOD; } @@ -403,16 +415,16 @@ verify_language_tag (string tag, vector& notes) try { LanguageTag test (tag); } catch (LanguageTagError &) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_LANGUAGE, tag)); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_LANGUAGE, tag}); } } -enum VerifyPictureAssetResult +enum class VerifyPictureAssetResult { - VERIFY_PICTURE_ASSET_RESULT_GOOD, - VERIFY_PICTURE_ASSET_RESULT_FRAME_NEARLY_TOO_LARGE, - VERIFY_PICTURE_ASSET_RESULT_BAD, + GOOD, + FRAME_NEARLY_TOO_LARGE, + BAD, }; @@ -431,9 +443,9 @@ biggest_frame_size (shared_ptr frame) template optional -verify_picture_asset_type (shared_ptr reel_mxf, function progress) +verify_picture_asset_type (shared_ptr reel_file_asset, function progress) { - auto asset = dynamic_pointer_cast(reel_mxf->asset_ref().asset()); + auto asset = dynamic_pointer_cast(reel_file_asset->asset_ref().asset()); if (!asset) { return optional(); } @@ -450,21 +462,21 @@ verify_picture_asset_type (shared_ptr reel_mxf, functionedit_rate().as_float())); static const int risky_frame = rint(230 * 1000000 / (8 * asset->edit_rate().as_float())); if (biggest_frame > max_frame) { - return VERIFY_PICTURE_ASSET_RESULT_BAD; + return VerifyPictureAssetResult::BAD; } else if (biggest_frame > risky_frame) { - return VERIFY_PICTURE_ASSET_RESULT_FRAME_NEARLY_TOO_LARGE; + return VerifyPictureAssetResult::FRAME_NEARLY_TOO_LARGE; } - return VERIFY_PICTURE_ASSET_RESULT_GOOD; + return VerifyPictureAssetResult::GOOD; } static VerifyPictureAssetResult -verify_picture_asset (shared_ptr reel_mxf, function progress) +verify_picture_asset (shared_ptr reel_file_asset, function progress) { - auto r = verify_picture_asset_type(reel_mxf, progress); + auto r = verify_picture_asset_type(reel_file_asset, progress); if (!r) { - r = verify_picture_asset_type(reel_mxf, progress); + r = verify_picture_asset_type(reel_file_asset, progress); } DCP_ASSERT (r); @@ -486,19 +498,15 @@ verify_main_picture_asset ( stage ("Checking picture asset hash", file); auto const r = verify_asset (dcp, reel_asset, progress); switch (r) { - case VERIFY_ASSET_RESULT_BAD: - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_ERROR, VerificationNote::INCORRECT_PICTURE_HASH, file - ) - ); + case VerifyAssetResult::BAD: + notes.push_back ({ + VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_PICTURE_HASH, file + }); break; - case VERIFY_ASSET_RESULT_CPL_PKL_DIFFER: - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_ERROR, VerificationNote::MISMATCHED_PICTURE_HASHES, file - ) - ); + case VerifyAssetResult::CPL_PKL_DIFFER: + notes.push_back ({ + VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_PICTURE_HASHES, file + }); break; default: break; @@ -506,19 +514,15 @@ verify_main_picture_asset ( stage ("Checking picture frame sizes", asset->file()); auto const pr = verify_picture_asset (reel_asset, progress); switch (pr) { - case VERIFY_PICTURE_ASSET_RESULT_BAD: - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_ERROR, VerificationNote::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file - ) - ); + case VerifyPictureAssetResult::BAD: + notes.push_back ({ + VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file + }); break; - case VERIFY_PICTURE_ASSET_RESULT_FRAME_NEARLY_TOO_LARGE: - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_WARNING, VerificationNote::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file - ) - ); + case VerifyPictureAssetResult::FRAME_NEARLY_TOO_LARGE: + notes.push_back ({ + VerificationNote::Type::WARNING, VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file + }); break; default: break; @@ -530,14 +534,12 @@ verify_main_picture_asset ( asset->size() != Size(1998, 1080) && asset->size() != Size(4096, 1716) && asset->size() != Size(3996, 2160)) { - notes.push_back( - VerificationNote( - VerificationNote::VERIFY_BV21_ERROR, - VerificationNote::INVALID_PICTURE_SIZE_IN_PIXELS, - String::compose("%1x%2", asset->size().width, asset->size().height), - file - ) - ); + notes.push_back({ + VerificationNote::Type::BV21_ERROR, + VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, + String::compose("%1x%2", asset->size().width, asset->size().height), + file + }); } /* Only 24, 25, 48fps allowed for 2K */ @@ -545,38 +547,33 @@ verify_main_picture_asset ( (asset->size() == Size(2048, 858) || asset->size() == Size(1998, 1080)) && (asset->edit_rate() != Fraction(24, 1) && asset->edit_rate() != Fraction(25, 1) && asset->edit_rate() != Fraction(48, 1)) ) { - notes.push_back( - VerificationNote( - VerificationNote::VERIFY_BV21_ERROR, - VerificationNote::INVALID_PICTURE_FRAME_RATE_FOR_2K, - String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), - file - ) - ); + notes.push_back({ + VerificationNote::Type::BV21_ERROR, + VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K, + String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), + file + }); } if (asset->size() == Size(4096, 1716) || asset->size() == Size(3996, 2160)) { /* Only 24fps allowed for 4K */ if (asset->edit_rate() != Fraction(24, 1)) { - notes.push_back( - VerificationNote( - VerificationNote::VERIFY_BV21_ERROR, - VerificationNote::INVALID_PICTURE_FRAME_RATE_FOR_4K, - String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), - file - ) - ); + notes.push_back({ + VerificationNote::Type::BV21_ERROR, + VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K, + String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), + file + }); } /* Only 2D allowed for 4K */ if (dynamic_pointer_cast(asset)) { - notes.push_back( - VerificationNote( - VerificationNote::VERIFY_BV21_ERROR, - VerificationNote::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D, - file - ) - ); + notes.push_back({ + VerificationNote::Type::BV21_ERROR, + VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D, + String::compose("%1/%2", asset->edit_rate().numerator, asset->edit_rate().denominator), + file + }); } } @@ -597,19 +594,11 @@ verify_main_sound_asset ( stage ("Checking sound asset hash", asset->file()); auto const r = verify_asset (dcp, reel_asset, progress); switch (r) { - case VERIFY_ASSET_RESULT_BAD: - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_ERROR, VerificationNote::INCORRECT_SOUND_HASH, *asset->file() - ) - ); + case VerifyAssetResult::BAD: + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_SOUND_HASH, *asset->file()}); break; - case VERIFY_ASSET_RESULT_CPL_PKL_DIFFER: - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_ERROR, VerificationNote::MISMATCHED_SOUND_HASHES, *asset->file() - ) - ); + case VerifyAssetResult::CPL_PKL_DIFFER: + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_SOUND_HASHES, *asset->file()}); break; default: break; @@ -619,11 +608,7 @@ verify_main_sound_asset ( verify_language_tag (asset->language(), notes); if (asset->sampling_rate() != 48000) { - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_SOUND_FRAME_RATE, *asset->file() - ) - ); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_SOUND_FRAME_RATE, raw_convert(asset->sampling_rate()), *asset->file()}); } } @@ -637,9 +622,9 @@ verify_main_subtitle_reel (shared_ptr reel_asset, vecto } if (!reel_asset->entry_point()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_SUBTITLE_ENTRY_POINT }); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT, reel_asset->id() }); } else if (reel_asset->entry_point().get()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INCORRECT_SUBTITLE_ENTRY_POINT }); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT, reel_asset->id() }); } } @@ -653,9 +638,9 @@ verify_closed_caption_reel (shared_ptr reel_asset, } if (!reel_asset->entry_point()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT }); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT, reel_asset->id() }); } else if (reel_asset->entry_point().get()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INCORRECT_CLOSED_CAPTION_ENTRY_POINT }); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT, reel_asset->id() }); } } @@ -680,13 +665,16 @@ verify_smpte_subtitle_asset ( if (!state.subtitle_language) { state.subtitle_language = language; } else if (state.subtitle_language != language) { - notes.push_back ({ VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISMATCHED_SUBTITLE_LANGUAGES }); + notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES }); } } else { - notes.push_back ({ VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_SUBTITLE_LANGUAGE, *asset->file() }); + notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, *asset->file() }); } - if (boost::filesystem::file_size(asset->file().get()) > 115 * 1024 * 1024) { - notes.push_back ({ VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_TIMED_TEXT_SIZE_IN_BYTES, *asset->file() }); + auto const size = boost::filesystem::file_size(asset->file().get()); + if (size > 115 * 1024 * 1024) { + notes.push_back ( + { VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, raw_convert(size), *asset->file() } + ); } /* XXX: I'm not sure what Bv2.1_7.2.1 means when it says "the font resource shall not be larger than 10MB" * but I'm hoping that checking for the total size of all fonts being <= 10MB will do. @@ -697,13 +685,13 @@ verify_smpte_subtitle_asset ( total_size += i.second.size(); } if (total_size > 10 * 1024 * 1024) { - notes.push_back ({ VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, asset->file().get() }); + notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, raw_convert(total_size), asset->file().get() }); } if (!asset->start_time()) { - notes.push_back ({ VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_SUBTITLE_START_TIME, asset->file().get() }); + notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_SUBTITLE_START_TIME, asset->file().get() }); } else if (asset->start_time() != Time()) { - notes.push_back ({ VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_SUBTITLE_START_TIME, asset->file().get() }); + notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_SUBTITLE_START_TIME, asset->file().get() }); } } @@ -742,18 +730,14 @@ verify_closed_caption_asset ( verify_subtitle_asset (asset, stage, xsd_dtd_directory, notes, state); if (asset->raw_xml().size() > 256 * 1024) { - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, *asset->file() - ) - ); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, raw_convert(asset->raw_xml().size()), *asset->file()}); } } static void -check_text_timing ( +verify_text_timing ( vector> reels, optional picture_frame_rate, vector& notes, @@ -814,27 +798,21 @@ check_text_timing ( } if (too_early) { - notes.push_back( - VerificationNote( - VerificationNote::VERIFY_WARNING, VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME - ) - ); + notes.push_back({ + VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME + }); } if (too_short) { - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_WARNING, VerificationNote::INVALID_SUBTITLE_DURATION - ) - ); + notes.push_back ({ + VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_DURATION + }); } if (too_close) { - notes.push_back ( - VerificationNote( - VerificationNote::VERIFY_WARNING, VerificationNote::INVALID_SUBTITLE_SPACING - ) - ); + notes.push_back ({ + VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_SPACING + }); } } @@ -849,7 +827,7 @@ struct LinesCharactersResult static void -check_text_lines_and_characters ( +verify_text_lines_and_characters ( shared_ptr asset, int warning_length, int error_length, @@ -880,11 +858,11 @@ check_text_lines_and_characters ( auto position = [](shared_ptr sub) { switch (sub->v_align()) { - case VALIGN_TOP: + case VAlign::TOP: return lrintf(sub->v_position() * 100); - case VALIGN_CENTER: + case VAlign::CENTER: return lrintf((0.5f + sub->v_position()) * 100); - case VALIGN_BOTTOM: + case VAlign::BOTTOM: return lrintf((1.0f - sub->v_position()) * 100); } @@ -940,7 +918,7 @@ check_text_lines_and_characters ( static void -check_text_timing (vector> reels, vector& notes) +verify_text_timing (vector> reels, vector& notes) { if (reels.empty()) { return; @@ -952,7 +930,7 @@ check_text_timing (vector> reels, vector& not } if (reels[0]->main_subtitle()) { - check_text_timing (reels, picture_frame_rate, notes, + verify_text_timing (reels, picture_frame_rate, notes, [](shared_ptr reel) { return static_cast(reel->main_subtitle()); }, @@ -966,7 +944,7 @@ check_text_timing (vector> reels, vector& not } for (auto i = 0U; i < reels[0]->closed_captions().size(); ++i) { - check_text_timing (reels, picture_frame_rate, notes, + verify_text_timing (reels, picture_frame_rate, notes, [i](shared_ptr reel) { return i < reel->closed_captions().size(); }, @@ -982,7 +960,7 @@ check_text_timing (vector> reels, vector& not void -check_extension_metadata (shared_ptr cpl, vector& notes) +verify_extension_metadata (shared_ptr cpl, vector& notes) { DCP_ASSERT (cpl->file()); cxml::Document doc ("CompositionPlaylist"); @@ -1032,9 +1010,9 @@ check_extension_metadata (shared_ptr cpl, vector& notes) } if (missing) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_EXTENSION_METADATA}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get()}); } else if (!malformed.empty()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_EXTENSION_METADATA, malformed}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_EXTENSION_METADATA, malformed, cpl->file().get()}); } } @@ -1044,11 +1022,11 @@ pkl_has_encrypted_assets (shared_ptr dcp, shared_ptr pkl) { vector encrypted; for (auto i: dcp->cpls()) { - for (auto j: i->reel_mxfs()) { + for (auto j: i->reel_file_assets()) { if (j->asset_ref().resolved()) { /* It's a bit surprising / broken but Interop subtitle assets are represented - * in reels by ReelSubtitleAsset which inherits ReelMXF, so it's possible for - * ReelMXFs to have assets which are not MXFs. + * in reels by ReelSubtitleAsset which inherits ReelFileAsset, so it's possible for + * ReelFileAssets to have assets which are not MXFs. */ if (auto asset = dynamic_pointer_cast(j->asset_ref().asset())) { if (asset->encrypted()) { @@ -1080,7 +1058,7 @@ dcp::verify ( xsd_dtd_directory = boost::filesystem::canonical (xsd_dtd_directory); vector notes; - State state; + State state{}; vector> dcps; for (auto i: directories) { @@ -1092,17 +1070,17 @@ dcp::verify ( try { dcp->read (¬es); } catch (ReadError& e) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::FAILED_READ, string(e.what()))); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } catch (XMLError& e) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::FAILED_READ, string(e.what()))); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } catch (MXFFileError& e) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::FAILED_READ, string(e.what()))); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } catch (cxml::Error& e) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::FAILED_READ, string(e.what()))); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } - if (dcp->standard() != SMPTE) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_STANDARD)); + if (dcp->standard() != Standard::SMPTE) { + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_STANDARD}); } for (auto cpl: dcp->cpls()) { @@ -1110,7 +1088,7 @@ dcp::verify ( validate_xml (cpl->file().get(), xsd_dtd_directory, notes); if (cpl->any_encrypted() && !cpl->all_encrypted()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::PARTIALLY_ENCRYPTED}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::PARTIALLY_ENCRYPTED}); } for (auto const& i: cpl->additional_subtitle_languages()) { @@ -1125,17 +1103,17 @@ dcp::verify ( LanguageTag::RegionSubtag test (terr); } catch (...) { if (terr != "001") { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_LANGUAGE, terr}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_LANGUAGE, terr}); } } } } - if (dcp->standard() == SMPTE) { + if (dcp->standard() == Standard::SMPTE) { if (!cpl->annotation_text()) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_CPL_ANNOTATION_TEXT)); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->id(), cpl->file().get()}); } else if (cpl->annotation_text().get() != cpl->content_title_text()) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_WARNING, VerificationNote::MISMATCHED_CPL_ANNOTATION_TEXT)); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->id(), cpl->file().get()}); } } @@ -1143,7 +1121,7 @@ dcp::verify ( /* Check that the CPL's hash corresponds to the PKL */ optional h = i->hash(cpl->id()); if (h && make_digest(ArrayData(*cpl->file())) != *h) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::MISMATCHED_CPL_HASHES)); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get()}); } /* Check that any PKL with a single CPL has its AnnotationText the same as the CPL's ContentTitleText */ @@ -1164,7 +1142,7 @@ dcp::verify ( } if (required_annotation_text && i->annotation_text() != required_annotation_text) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, i->file().get()}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, i->id(), i->file().get()}); } } @@ -1183,24 +1161,24 @@ dcp::verify ( for (auto i: reel->assets()) { if (i->duration() && (i->duration().get() * i->edit_rate().denominator / i->edit_rate().numerator) < 1) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::INVALID_DURATION, i->id())); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_DURATION, i->id()}); } if ((i->intrinsic_duration() * i->edit_rate().denominator / i->edit_rate().numerator) < 1) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::INVALID_INTRINSIC_DURATION, i->id())); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_INTRINSIC_DURATION, i->id()}); } - auto mxf = dynamic_pointer_cast(i); - if (mxf && !mxf->hash()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_HASH, i->id()}); + auto file_asset = dynamic_pointer_cast(i); + if (file_asset && !file_asset->hash()) { + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_HASH, i->id()}); } } - if (dcp->standard() == SMPTE) { + if (dcp->standard() == Standard::SMPTE) { boost::optional duration; for (auto i: reel->assets()) { if (!duration) { duration = i->actual_duration(); } else if (*duration != i->actual_duration()) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISMATCHED_ASSET_DURATION)); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_ASSET_DURATION}); break; } } @@ -1217,7 +1195,11 @@ dcp::verify ( frame_rate.numerator != 50 && frame_rate.numerator != 60 && frame_rate.numerator != 96)) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::INVALID_PICTURE_FRAME_RATE)); + notes.push_back ({ + VerificationNote::Type::ERROR, + VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, + String::compose("%1/%2", frame_rate.numerator, frame_rate.denominator) + }); } /* Check asset */ if (reel->main_picture()->asset_ref().resolved()) { @@ -1256,90 +1238,93 @@ dcp::verify ( most_closed_captions = std::max (most_closed_captions, reel->closed_captions().size()); } - if (dcp->standard() == SMPTE) { + if (dcp->standard() == Standard::SMPTE) { if (have_main_subtitle && have_no_main_subtitle) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS}); } if (fewest_closed_captions != most_closed_captions) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS}); } - if (cpl->content_kind() == FEATURE) { + if (cpl->content_kind() == ContentKind::FEATURE) { if (markers_seen.find(Marker::FFEC) == markers_seen.end()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_FFEC_IN_FEATURE}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_FFEC_IN_FEATURE}); } if (markers_seen.find(Marker::FFMC) == markers_seen.end()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_FFMC_IN_FEATURE}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_FFMC_IN_FEATURE}); } } auto ffoc = markers_seen.find(Marker::FFOC); if (ffoc == markers_seen.end()) { - notes.push_back ({VerificationNote::VERIFY_WARNING, VerificationNote::MISSING_FFOC}); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSING_FFOC}); } else if (ffoc->second.e != 1) { - notes.push_back ({VerificationNote::VERIFY_WARNING, VerificationNote::INCORRECT_FFOC}); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_FFOC, raw_convert(ffoc->second.e)}); } auto lfoc = markers_seen.find(Marker::LFOC); if (lfoc == markers_seen.end()) { - notes.push_back ({VerificationNote::VERIFY_WARNING, VerificationNote::MISSING_LFOC}); - } else if (lfoc->second.as_editable_units(lfoc->second.tcr) != (cpl->reels().back()->duration() - 1)) { - notes.push_back ({VerificationNote::VERIFY_WARNING, VerificationNote::INCORRECT_LFOC}); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSING_LFOC}); + } else { + auto lfoc_time = lfoc->second.as_editable_units(lfoc->second.tcr); + if (lfoc_time != (cpl->reels().back()->duration() - 1)) { + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::INCORRECT_LFOC, raw_convert(lfoc_time)}); + } } - check_text_timing (cpl->reels(), notes); + verify_text_timing (cpl->reels(), notes); LinesCharactersResult result; for (auto reel: cpl->reels()) { if (reel->main_subtitle() && reel->main_subtitle()->asset()) { - check_text_lines_and_characters (reel->main_subtitle()->asset(), 52, 79, &result); + verify_text_lines_and_characters (reel->main_subtitle()->asset(), 52, 79, &result); } } if (result.line_count_exceeded) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_WARNING, VerificationNote::INVALID_SUBTITLE_LINE_COUNT)); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT}); } if (result.error_length_exceeded) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_WARNING, VerificationNote::INVALID_SUBTITLE_LINE_LENGTH)); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH}); } else if (result.warning_length_exceeded) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_WARNING, VerificationNote::NEARLY_INVALID_SUBTITLE_LINE_LENGTH)); + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH}); } result = LinesCharactersResult(); for (auto reel: cpl->reels()) { for (auto i: reel->closed_captions()) { if (i->asset()) { - check_text_lines_and_characters (i->asset(), 32, 32, &result); + verify_text_lines_and_characters (i->asset(), 32, 32, &result); } } } if (result.line_count_exceeded) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_CLOSED_CAPTION_LINE_COUNT)); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT}); } if (result.error_length_exceeded) { - notes.push_back (VerificationNote(VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_CLOSED_CAPTION_LINE_LENGTH)); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH}); } if (!cpl->full_content_title_text()) { /* Since FullContentTitleText is assumed always to exist if there's a CompositionMetadataAsset we * can use it as a proxy for CompositionMetadataAsset's existence. */ - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_CPL_METADATA}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get()}); } else if (!cpl->version_number()) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get()}); } - check_extension_metadata (cpl, notes); + verify_extension_metadata (cpl, notes); if (cpl->any_encrypted()) { cxml::Document doc ("CompositionPlaylist"); DCP_ASSERT (cpl->file()); doc.read_file (cpl->file().get()); if (!doc.optional_node_child("Signature")) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, cpl->file().get()}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, cpl->id(), cpl->file().get()}); } } } @@ -1352,7 +1337,7 @@ dcp::verify ( cxml::Document doc ("PackingList"); doc.read_file (pkl->file().get()); if (!doc.optional_node_child("Signature")) { - notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, pkl->file().get()}); + notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, pkl->id(), pkl->file().get()}); } } } @@ -1361,142 +1346,153 @@ dcp::verify ( stage ("Checking ASSETMAP", dcp->asset_map_path().get()); validate_xml (dcp->asset_map_path().get(), xsd_dtd_directory, notes); } else { - notes.push_back (VerificationNote(VerificationNote::VERIFY_ERROR, VerificationNote::MISSING_ASSETMAP)); + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::MISSING_ASSETMAP}); } } return notes; } + string dcp::note_to_string (VerificationNote note) { + /** These strings should say what is wrong, incorporating any extra details (ID, filenames etc.). + * + * e.g. "ClosedCaption asset has no tag.", + * not "ClosedCaption assets must have an tag." + * + * It's OK to use XML tag names where they are clear. + * If both ID and filename are available, use only the ID. + * End messages with a full stop. + * Messages should not mention whether or not their errors are a part of Bv2.1. + */ switch (note.code()) { - case VerificationNote::FAILED_READ: + case VerificationNote::Code::FAILED_READ: return *note.note(); - case VerificationNote::MISMATCHED_CPL_HASHES: - return "The hash of the CPL in the PKL does not agree with the CPL file."; - case VerificationNote::INVALID_PICTURE_FRAME_RATE: - return "The picture in a reel has an invalid frame rate."; - case VerificationNote::INCORRECT_PICTURE_HASH: + case VerificationNote::Code::MISMATCHED_CPL_HASHES: + return String::compose("The hash of the CPL %1 in the PKL does not agree with the CPL file.", note.note().get()); + case VerificationNote::Code::INVALID_PICTURE_FRAME_RATE: + return String::compose("The picture in a reel has an invalid frame rate %1.", note.note().get()); + case VerificationNote::Code::INCORRECT_PICTURE_HASH: return String::compose("The hash of the picture asset %1 does not agree with the PKL file.", note.file()->filename()); - case VerificationNote::MISMATCHED_PICTURE_HASHES: + case VerificationNote::Code::MISMATCHED_PICTURE_HASHES: return String::compose("The PKL and CPL hashes differ for the picture asset %1.", note.file()->filename()); - case VerificationNote::INCORRECT_SOUND_HASH: + case VerificationNote::Code::INCORRECT_SOUND_HASH: return String::compose("The hash of the sound asset %1 does not agree with the PKL file.", note.file()->filename()); - case VerificationNote::MISMATCHED_SOUND_HASHES: + case VerificationNote::Code::MISMATCHED_SOUND_HASHES: return String::compose("The PKL and CPL hashes differ for the sound asset %1.", note.file()->filename()); - case VerificationNote::EMPTY_ASSET_PATH: + case VerificationNote::Code::EMPTY_ASSET_PATH: return "The asset map contains an empty asset path."; - case VerificationNote::MISSING_ASSET: - return String::compose("The file for an asset in the asset map cannot be found; missing file is %1.", note.file()->filename()); - case VerificationNote::MISMATCHED_STANDARD: + case VerificationNote::Code::MISSING_ASSET: + return String::compose("The file %1 for an asset in the asset map cannot be found.", note.file()->filename()); + case VerificationNote::Code::MISMATCHED_STANDARD: return "The DCP contains both SMPTE and Interop parts."; - case VerificationNote::INVALID_XML: + case VerificationNote::Code::INVALID_XML: return String::compose("An XML file is badly formed: %1 (%2:%3)", note.note().get(), note.file()->filename(), note.line().get()); - case VerificationNote::MISSING_ASSETMAP: + case VerificationNote::Code::MISSING_ASSETMAP: return "No ASSETMAP or ASSETMAP.xml was found."; - case VerificationNote::INVALID_INTRINSIC_DURATION: - return String::compose("The intrinsic duration of an asset is less than 1 second long: %1", note.note().get()); - case VerificationNote::INVALID_DURATION: - return String::compose("The duration of an asset is less than 1 second long: %1", note.note().get()); - case VerificationNote::INVALID_PICTURE_FRAME_SIZE_IN_BYTES: + case VerificationNote::Code::INVALID_INTRINSIC_DURATION: + return String::compose("The intrinsic duration of the asset %1 is less than 1 second long.", note.note().get()); + case VerificationNote::Code::INVALID_DURATION: + return String::compose("The duration of the asset %1 is less than 1 second long.", note.note().get()); + case VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES: return String::compose("The instantaneous bit rate of the picture asset %1 is larger than the limit of 250Mbit/s in at least one place.", note.file()->filename()); - case VerificationNote::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES: + case VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES: return String::compose("The instantaneous bit rate of the picture asset %1 is close to the limit of 250Mbit/s in at least one place.", note.file()->filename()); - case VerificationNote::EXTERNAL_ASSET: - return String::compose("An asset that this DCP refers to is not included in the DCP. It may be a VF. Missing asset is %1.", note.note().get()); - case VerificationNote::INVALID_STANDARD: - return "This DCP does not use the SMPTE standard, which is required for Bv2.1 compliance."; - case VerificationNote::INVALID_LANGUAGE: + case VerificationNote::Code::EXTERNAL_ASSET: + return String::compose("The asset %1 that this DCP refers to is not included in the DCP. It may be a VF.", note.note().get()); + case VerificationNote::Code::INVALID_STANDARD: + return "This DCP does not use the SMPTE standard."; + case VerificationNote::Code::INVALID_LANGUAGE: return String::compose("The DCP specifies a language '%1' which does not conform to the RFC 5646 standard.", note.note().get()); - case VerificationNote::INVALID_PICTURE_SIZE_IN_PIXELS: - return String::compose("A picture asset's size (%1) is not one of those allowed by Bv2.1 (2048x858, 1998x1080, 4096x1716 or 3996x2160)", note.note().get()); - case VerificationNote::INVALID_PICTURE_FRAME_RATE_FOR_2K: - return String::compose("A picture asset's frame rate (%1) is not one of those allowed for 2K DCPs by Bv2.1 (24, 25 or 48fps)", note.note().get()); - case VerificationNote::INVALID_PICTURE_FRAME_RATE_FOR_4K: - return String::compose("A picture asset's frame rate (%1) is not 24fps as required for 4K DCPs by Bv2.1", note.note().get()); - case VerificationNote::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D: - return "3D 4K DCPs are not allowed by Bv2.1"; - case VerificationNote::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES: - return String::compose("The XML for the closed caption asset %1 is longer than the 256KB maximum required by Bv2.1", note.file()->filename()); - case VerificationNote::INVALID_TIMED_TEXT_SIZE_IN_BYTES: - return String::compose("The total size of the timed text asset %1 is larger than the 115MB maximum required by Bv2.1", note.file()->filename()); - case VerificationNote::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES: - return String::compose("The total size of the fonts in timed text asset %1 is larger than the 10MB maximum required by Bv2.1", note.file()->filename()); - case VerificationNote::MISSING_SUBTITLE_LANGUAGE: - return String::compose("The XML for a SMPTE subtitle asset has no tag, which is required by Bv2.1", note.file()->filename()); - case VerificationNote::MISMATCHED_SUBTITLE_LANGUAGES: + case VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS: + return String::compose("The size %1 of picture asset %2 is not allowed.", note.note().get(), note.file()->filename()); + case VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K: + return String::compose("The frame rate %1 of picture asset %2 is not allowed for 2K DCPs.", note.note().get(), note.file()->filename()); + case VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K: + return String::compose("The frame rate %1 of picture asset %2 is not allowed for 4K DCPs.", note.note().get(), note.file()->filename()); + case VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D: + return "3D 4K DCPs are not allowed."; + case VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES: + return String::compose("The size %1 of the closed caption asset %2 is larger than the 256KB maximum.", note.note().get(), note.file()->filename()); + case VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES: + return String::compose("The size %1 of the timed text asset %2 is larger than the 115MB maximum.", note.note().get(), note.file()->filename()); + case VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES: + return String::compose("The size %1 of the fonts in timed text asset %2 is larger than the 10MB maximum.", note.note().get(), note.file()->filename()); + case VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE: + return String::compose("The XML for the SMPTE subtitle asset %1 has no tag.", note.file()->filename()); + case VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES: return "Some subtitle assets have different tags than others"; - case VerificationNote::MISSING_SUBTITLE_START_TIME: - return String::compose("The XML for a SMPTE subtitle asset has no tag, which is required by Bv2.1", note.file()->filename()); - case VerificationNote::INVALID_SUBTITLE_START_TIME: - return String::compose("The XML for a SMPTE subtitle asset has a non-zero tag, which is disallowed by Bv2.1", note.file()->filename()); - case VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME: + case VerificationNote::Code::MISSING_SUBTITLE_START_TIME: + return String::compose("The XML for the SMPTE subtitle asset %1 has no tag.", note.file()->filename()); + case VerificationNote::Code::INVALID_SUBTITLE_START_TIME: + return String::compose("The XML for a SMPTE subtitle asset %1 has a non-zero tag.", note.file()->filename()); + case VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME: return "The first subtitle or closed caption is less than 4 seconds from the start of the DCP."; - case VerificationNote::INVALID_SUBTITLE_DURATION: - return "At least one subtitle is less than the minimum of 15 frames suggested by Bv2.1"; - case VerificationNote::INVALID_SUBTITLE_SPACING: - return "At least one pair of subtitles are separated by less than the the minimum of 2 frames suggested by Bv2.1"; - case VerificationNote::INVALID_SUBTITLE_LINE_COUNT: - return "There are more than 3 subtitle lines in at least one place in the DCP, which Bv2.1 advises against."; - case VerificationNote::NEARLY_INVALID_SUBTITLE_LINE_LENGTH: - return "There are more than 52 characters in at least one subtitle line, which Bv2.1 advises against."; - case VerificationNote::INVALID_SUBTITLE_LINE_LENGTH: - return "There are more than 79 characters in at least one subtitle line, which Bv2.1 strongly advises against."; - case VerificationNote::INVALID_CLOSED_CAPTION_LINE_COUNT: - return "There are more than 3 closed caption lines in at least one place, which is disallowed by Bv2.1"; - case VerificationNote::INVALID_CLOSED_CAPTION_LINE_LENGTH: - return "There are more than 32 characters in at least one closed caption line, which is disallowed by Bv2.1"; - case VerificationNote::INVALID_SOUND_FRAME_RATE: - return "A sound asset has a sampling rate other than 48kHz, which is disallowed by Bv2.1"; - case VerificationNote::MISSING_CPL_ANNOTATION_TEXT: - return "The CPL has no tag, which is required by Bv2.1"; - case VerificationNote::MISMATCHED_CPL_ANNOTATION_TEXT: - return "The CPL's differs from its , which Bv2.1 advises against."; - case VerificationNote::MISMATCHED_ASSET_DURATION: - return "All assets in a reel do not have the same duration, which is required by Bv2.1"; - case VerificationNote::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS: + case VerificationNote::Code::INVALID_SUBTITLE_DURATION: + return "At least one subtitle lasts less than 15 frames."; + case VerificationNote::Code::INVALID_SUBTITLE_SPACING: + return "At least one pair of subtitles is separated by less than 2 frames."; + case VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT: + return "There are more than 3 subtitle lines in at least one place in the DCP."; + case VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH: + return "There are more than 52 characters in at least one subtitle line."; + case VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH: + return "There are more than 79 characters in at least one subtitle line."; + case VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT: + return "There are more than 3 closed caption lines in at least one place."; + case VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH: + return "There are more than 32 characters in at least one closed caption line."; + case VerificationNote::Code::INVALID_SOUND_FRAME_RATE: + return String::compose("The sound asset %1 has a sampling rate of %2", note.file()->filename(), note.note().get()); + case VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT: + return String::compose("The CPL %1 has no tag.", note.note().get()); + case VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT: + return String::compose("The CPL %1 has an which differs from its ", note.note().get()); + case VerificationNote::Code::MISMATCHED_ASSET_DURATION: + return "All assets in a reel do not have the same duration."; + case VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS: return "At least one reel contains a subtitle asset, but some reel(s) do not"; - case VerificationNote::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS: + case VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS: return "At least one reel has closed captions, but reels have different numbers of closed caption assets."; - case VerificationNote::MISSING_SUBTITLE_ENTRY_POINT: - return "Subtitle assets must have an tag."; - case VerificationNote::INCORRECT_SUBTITLE_ENTRY_POINT: - return "Subtitle assets must have an of 0."; - case VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT: - return "Closed caption assets must have an tag."; - case VerificationNote::INCORRECT_CLOSED_CAPTION_ENTRY_POINT: - return "Closed caption assets must have an of 0."; - case VerificationNote::MISSING_HASH: - return String::compose("An asset is missing a tag: %1", note.note().get()); - case VerificationNote::MISSING_FFEC_IN_FEATURE: + case VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT: + return String::compose("The subtitle asset %1 has no tag.", note.note().get()); + case VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT: + return String::compose("The subtitle asset %1 has an other than 0.", note.note().get()); + case VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT: + return String::compose("The closed caption asset %1 has no tag.", note.note().get()); + case VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT: + return String::compose("The closed caption asset %1 has an other than 0.", note.note().get()); + case VerificationNote::Code::MISSING_HASH: + return String::compose("The asset %1 has no tag in the CPL.", note.note().get()); + case VerificationNote::Code::MISSING_FFEC_IN_FEATURE: return "The DCP is marked as a Feature but there is no FFEC (first frame of end credits) marker"; - case VerificationNote::MISSING_FFMC_IN_FEATURE: + case VerificationNote::Code::MISSING_FFMC_IN_FEATURE: return "The DCP is marked as a Feature but there is no FFMC (first frame of moving credits) marker"; - case VerificationNote::MISSING_FFOC: + case VerificationNote::Code::MISSING_FFOC: return "There should be a FFOC (first frame of content) marker"; - case VerificationNote::MISSING_LFOC: + case VerificationNote::Code::MISSING_LFOC: return "There should be a LFOC (last frame of content) marker"; - case VerificationNote::INCORRECT_FFOC: - return "The FFOC marker should bet set to 1"; - case VerificationNote::INCORRECT_LFOC: - return "The LFOC marker should be set to 1 less than the duration of the last reel"; - case VerificationNote::MISSING_CPL_METADATA: - return "There should be a tag"; - case VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER: - return "The CPL metadata must contain a "; - case VerificationNote::MISSING_EXTENSION_METADATA: - return "The CPL metadata must contain "; - case VerificationNote::INVALID_EXTENSION_METADATA: - return String::compose("The is malformed in some way: %1", note.note().get()); - case VerificationNote::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT: - return String::compose("The CPL %1, which has encrypted content, is not signed", note.file()->filename()); - case VerificationNote::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT: - return String::compose("The PKL %1, which has encrypted content, is not signed", note.file()->filename()); - case VerificationNote::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL: - return String::compose("The PKL %1 has only one CPL but its does not match the CPL's ", note.file()->filename()); - case VerificationNote::PARTIALLY_ENCRYPTED: + case VerificationNote::Code::INCORRECT_FFOC: + return String::compose("The FFOC marker is %1 instead of 1", note.note().get()); + case VerificationNote::Code::INCORRECT_LFOC: + return String::compose("The LFOC marker is %1 instead of 1 less than the duration of the last reel.", note.note().get()); + case VerificationNote::Code::MISSING_CPL_METADATA: + return String::compose("The CPL %1 has no tag.", note.note().get()); + case VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER: + return String::compose("The CPL %1 has no in its .", note.note().get()); + case VerificationNote::Code::MISSING_EXTENSION_METADATA: + return String::compose("The CPL %1 has no in its .", note.note().get()); + case VerificationNote::Code::INVALID_EXTENSION_METADATA: + return String::compose("The CPL %1 has a malformed (%2).", note.file()->filename(), note.note().get()); + case VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT: + return String::compose("The CPL %1, which has encrypted content, is not signed.", note.note().get()); + case VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT: + return String::compose("The PKL %1, which has encrypted content, is not signed.", note.note().get()); + case VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL: + return String::compose("The PKL %1 has only one CPL but its does not match the CPL's ", note.note().get()); + case VerificationNote::Code::PARTIALLY_ENCRYPTED: return "Some assets are encrypted but some are not"; } @@ -1510,6 +1506,7 @@ dcp::operator== (dcp::VerificationNote const& a, dcp::VerificationNote const& b) return a.type() == b.type() && a.code() == b.code() && a.note() == b.note() && a.file() == b.file() && a.line() == b.line(); } + std::ostream& dcp::operator<< (std::ostream& s, dcp::VerificationNote const& note) {