summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2021-01-18 17:06:23 +0100
committerCarl Hetherington <cth@carlh.net>2021-01-18 17:06:23 +0100
commitb2e68c20550fce629d9ebaf1fca5244d1e2ca517 (patch)
tree70ec866c91e440d82cd885e723f8bf0afb9ec137
parent36310b78f8fd84554f2c87dc513bd04efe0fd69a (diff)
Bv2.1 8.6.3: <ExtensionMetadata> must be present and have precise contents.
-rw-r--r--src/verify.cc64
-rw-r--r--src/verify.h4
-rw-r--r--test/verify_test.cc217
3 files changed, 273 insertions, 12 deletions
diff --git a/src/verify.cc b/src/verify.cc
index d5b80b9f..58e777fb 100644
--- a/src/verify.cc
+++ b/src/verify.cc
@@ -1003,6 +1003,64 @@ check_text_timing (vector<shared_ptr<dcp::Reel>> reels, vector<VerificationNote>
}
+void
+check_extension_metadata (shared_ptr<dcp::CPL> cpl, vector<VerificationNote>& notes)
+{
+ DCP_ASSERT (cpl->file());
+ cxml::Document doc ("CompositionPlaylist");
+ doc.read_file (cpl->file().get());
+
+ auto missing = false;
+ string malformed;
+
+ if (auto reel_list = doc.node_child("ReelList")) {
+ auto reels = reel_list->node_children("Reel");
+ if (!reels.empty()) {
+ if (auto asset_list = reels[0]->optional_node_child("AssetList")) {
+ if (auto metadata = asset_list->optional_node_child("CompositionMetadataAsset")) {
+ if (auto extension_list = metadata->optional_node_child("ExtensionMetadataList")) {
+ missing = true;
+ for (auto extension: extension_list->node_children("ExtensionMetadata")) {
+ if (extension->optional_string_attribute("scope").get_value_or("") != "http://isdcf.com/ns/cplmd/app") {
+ continue;
+ }
+ missing = false;
+ if (auto name = extension->optional_node_child("Name")) {
+ if (name->content() != "Application") {
+ malformed = "<Name> should be 'Application'";
+ }
+ }
+ if (auto property_list = extension->optional_node_child("PropertyList")) {
+ if (auto property = property_list->optional_node_child("Property")) {
+ if (auto name = property->optional_node_child("Name")) {
+ if (name->content() != "DCP Constraints Profile") {
+ malformed = "<Name> property should be 'DCP Constraints Profile'";
+ }
+ }
+ if (auto value = property->optional_node_child("Value")) {
+ if (value->content() != "SMPTE-RDD-52:2020-Bv2.1") {
+ malformed = "<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'";
+ }
+ }
+ }
+ }
+ }
+ } else {
+ missing = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (missing) {
+ notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_EXTENSION_METADATA});
+ } else if (!malformed.empty()) {
+ notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::INVALID_EXTENSION_METADATA, malformed});
+ }
+}
+
+
vector<VerificationNote>
dcp::verify (
vector<boost::filesystem::path> directories,
@@ -1240,6 +1298,8 @@ dcp::verify (
} else if (!cpl->version_number()) {
notes.push_back ({VerificationNote::VERIFY_BV21_ERROR, VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER});
}
+
+ check_extension_metadata (cpl, notes);
}
}
@@ -1377,6 +1437,10 @@ dcp::note_to_string (dcp::VerificationNote note)
return "There should be a <CompositionMetadataAsset> tag";
case dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER:
return "The CPL metadata must contain a <VersionNumber>";
+ case dcp::VerificationNote::MISSING_EXTENSION_METADATA:
+ return "The CPL metadata must contain <ExtensionMetadata>";
+ case dcp::VerificationNote::INVALID_EXTENSION_METADATA:
+ return String::compose("The <ExtensionMetadata> is malformed in some way: %1", note.note().get());
}
return "";
diff --git a/src/verify.h b/src/verify.h
index 65095a8f..60100435 100644
--- a/src/verify.h
+++ b/src/verify.h
@@ -169,6 +169,10 @@ public:
MISSING_CPL_METADATA,
/** CPL metadata should contain <VersionNumber> of 1, at least */
MISSING_CPL_METADATA_VERSION_NUMBER,
+ /** There must be an <ExtensionMetadata> in <CompositionMetadataAsset> Bv2.1_8.6.3 */
+ MISSING_EXTENSION_METADATA,
+ /** <ExtensionMetadata> must have a particular form Bv2.1_8.6.3 */
+ INVALID_EXTENSION_METADATA,
};
VerificationNote (Type type, Code code)
diff --git a/test/verify_test.cc b/test/verify_test.cc
index fd76209b..34f95aad 100644
--- a/test/verify_test.cc
+++ b/test/verify_test.cc
@@ -148,6 +148,27 @@ public:
BOOST_REQUIRE (_content != old_content);
}
+ void delete_lines (string from, string to)
+ {
+ vector<string> lines;
+ boost::algorithm::split (lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on);
+ bool deleting = false;
+ auto old_content = _content;
+ _content = "";
+ for (auto i: lines) {
+ if (i.find(from) != string::npos) {
+ deleting = true;
+ }
+ if (!deleting) {
+ _content += i + "\n";
+ }
+ if (deleting && i.find(to) != string::npos) {
+ deleting = false;
+ }
+ }
+ BOOST_REQUIRE (_content != old_content);
+ }
+
private:
boost::filesystem::path _path;
std::string _content;
@@ -166,18 +187,10 @@ dump_notes (vector<dcp::VerificationNote> const & notes)
static
void
-check_verify_result (vector<boost::filesystem::path> dir, vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes)
+check_verify_result (vector<boost::filesystem::path> dir, vector<dcp::VerificationNote> test_notes)
{
auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
- BOOST_REQUIRE_EQUAL (notes.size(), types_and_codes.size());
- auto i = notes.begin();
- auto j = types_and_codes.begin();
- while (i != notes.end()) {
- BOOST_CHECK_EQUAL (i->type(), j->first);
- BOOST_CHECK_EQUAL (i->code(), j->second);
- ++i;
- ++j;
- }
+ BOOST_REQUIRE_EQUAL (notes.size(), test_notes.size());
}
@@ -2057,7 +2070,7 @@ void
verify_markers_test (
boost::filesystem::path dir,
vector<pair<dcp::Marker, dcp::Time>> markers,
- vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes
+ vector<dcp::VerificationNote> test_notes
)
{
auto dcp = make_simple (dir);
@@ -2068,7 +2081,7 @@ verify_markers_test (
}
dcp->cpls()[0]->reels()[0]->add(markers_asset);
dcp->write_xml (dcp::SMPTE);
- check_verify_result ({dir}, types_and_codes);
+ check_verify_result ({dir}, test_notes);
}
@@ -2165,3 +2178,183 @@ BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});
}
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata1)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata1";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata2)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata2";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata3)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata3";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("<meta:Name>A", "<meta:NameX>A");
+ e.replace ("n</meta:Name>", "n</meta:NameX>");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata4)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata4";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("Application", "Fred");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'Application'") },
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata5)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata5";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("DCP Constraints Profile", "Fred");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'") },
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata6)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata6";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("<meta:Value>", "<meta:ValueX>");
+ e.replace ("</meta:Value>", "</meta:ValueX>");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata7)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata7";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'") },
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata8)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata8";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("<meta:Property>", "<meta:PropertyX>");
+ e.replace ("</meta:Property>", "</meta:PropertyX>");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ });
+}
+
+
+BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata9)
+{
+ boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata9";
+ auto dcp = make_simple (dir);
+ dcp->write_xml (dcp::SMPTE);
+ {
+ Editor e (dcp->cpls()[0]->file().get());
+ e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
+ e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
+ }
+
+ check_verify_result (
+ {dir},
+ {
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
+ { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
+ });
+}
+
+