Make similar changes to the previous commit for _xml_id.
[libdcp.git] / src / verify.cc
index a2298f385ce98dae4b93f62d1196d5796ffdcad1..5550a67f21867fa9f99efa843c02d6a1cbeacf39 100644 (file)
@@ -156,22 +156,22 @@ private:
 class DCPErrorHandler : public ErrorHandler
 {
 public:
-       void warning(const SAXParseException& e)
+       void warning(const SAXParseException& e) override
        {
                maybe_add (XMLValidationError(e));
        }
 
-       void error(const SAXParseException& e)
+       void error(const SAXParseException& e) override
        {
                maybe_add (XMLValidationError(e));
        }
 
-       void fatalError(const SAXParseException& e)
+       void fatalError(const SAXParseException& e) override
        {
                maybe_add (XMLValidationError(e));
        }
 
-       void resetErrors() {
+       void resetErrors() override {
                _errors.clear ();
        }
 
@@ -246,7 +246,7 @@ public:
                add("http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL", "SMPTE-429-10-2008.xsd");
        }
 
-       InputSource* resolveEntity(XMLCh const *, XMLCh const * system_id)
+       InputSource* resolveEntity(XMLCh const *, XMLCh const * system_id) override
        {
                if (!system_id) {
                        return 0;
@@ -703,12 +703,17 @@ verify_smpte_subtitle_asset (
        }
 
        DCP_ASSERT (asset->resource_id());
-       if (asset->resource_id().get() != asset->xml_id()) {
-               notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID });
-       }
+       auto xml_id = asset->xml_id();
+       if (xml_id) {
+               if (asset->resource_id().get() != xml_id) {
+                       notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID });
+               }
 
-       if (asset->id() == asset->resource_id().get() || asset->id() == asset->xml_id()) {
-               notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID });
+               if (asset->id() == asset->resource_id().get() || asset->id() == xml_id) {
+                       notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID });
+               }
+       } else {
+               notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED});
        }
 }
 
@@ -728,7 +733,11 @@ verify_subtitle_asset (
        /* Note: we must not use SubtitleAsset::xml_as_string() here as that will mean the data on disk
         * gets passed through libdcp which may clean up and therefore hide errors.
         */
-       validate_xml (asset->raw_xml(), xsd_dtd_directory, notes);
+       if (asset->raw_xml()) {
+               validate_xml (asset->raw_xml().get(), xsd_dtd_directory, notes);
+       } else {
+               notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED});
+       }
 
        auto smpte = dynamic_pointer_cast<const SMPTESubtitleAsset>(asset);
        if (smpte) {
@@ -752,16 +761,20 @@ verify_closed_caption_asset (
        /* Note: we must not use SubtitleAsset::xml_as_string() here as that will mean the data on disk
         * gets passed through libdcp which may clean up and therefore hide errors.
         */
-       validate_xml (asset->raw_xml(), xsd_dtd_directory, notes);
+       auto raw_xml = asset->raw_xml();
+       if (raw_xml) {
+               validate_xml (*raw_xml, xsd_dtd_directory, notes);
+               if (raw_xml->size() > 256 * 1024) {
+                       notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, raw_convert<string>(raw_xml->size()), *asset->file()});
+               }
+       } else {
+               notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED});
+       }
 
        auto smpte = dynamic_pointer_cast<const SMPTESubtitleAsset>(asset);
        if (smpte) {
                verify_smpte_timed_text_asset (smpte, reel_asset_duration, notes);
        }
-
-       if (asset->raw_xml().size() > 256 * 1024) {
-               notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES, raw_convert<string>(asset->raw_xml().size()), *asset->file()});
-       }
 }
 
 
@@ -772,7 +785,7 @@ verify_text_timing (
        int edit_rate,
        vector<VerificationNote>& notes,
        std::function<bool (shared_ptr<Reel>)> check,
-       std::function<string (shared_ptr<Reel>)> xml,
+       std::function<optional<string> (shared_ptr<Reel>)> xml,
        std::function<int64_t (shared_ptr<Reel>)> duration
        )
 {
@@ -823,6 +836,12 @@ verify_text_timing (
                        continue;
                }
 
+               auto reel_xml = xml(reels[i]);
+               if (!reel_xml) {
+                       notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED});
+                       continue;
+               }
+
                /* We need to look at <Subtitle> instances in the XML being checked, so we can't use the subtitles
                 * read in by libdcp's parser.
                 */
@@ -832,7 +851,7 @@ verify_text_timing (
                optional<Time> start_time;
                try {
                        doc = make_shared<cxml::Document>("SubtitleReel");
-                       doc->read_string (xml(reels[i]));
+                       doc->read_string (*reel_xml);
                        tcr = doc->number_child<int>("TimeCodeRate");
                        auto start_time_string = doc->optional_string_child("StartTime");
                        if (start_time_string) {
@@ -840,7 +859,7 @@ verify_text_timing (
                        }
                } catch (...) {
                        doc = make_shared<cxml::Document>("DCSubtitle");
-                       doc->read_string (xml(reels[i]));
+                       doc->read_string (*reel_xml);
                }
                parse (doc, tcr, start_time, edit_rate, i == 0);
                auto end = reel_offset + duration(reels[i]);
@@ -1131,7 +1150,7 @@ dcp::verify (
                stage ("Checking DCP", dcp->directory());
                bool carry_on = true;
                try {
-                       dcp->read (&notes);
+                       dcp->read (&notes, true);
                } catch (MissingAssetmapError& e) {
                        notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())});
                        carry_on = false;
@@ -1472,6 +1491,8 @@ dcp::note_to_string (VerificationNote note)
                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::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::THREED_ASSET_MARKED_AS_TWOD:
+               return String::compose("The asset %1 is 3D but its MXF is marked as 2D.", note.file()->filename());
        case VerificationNote::Code::INVALID_STANDARD:
                return "This DCP does not use the SMPTE standard.";
        case VerificationNote::Code::INVALID_LANGUAGE:
@@ -1603,6 +1624,8 @@ dcp::note_to_string (VerificationNote note)
                DCP_ASSERT (parts.size() == 2);
                return String::compose("The reel duration of some timed text (%1) is not the same as the ContainerDuration of its MXF (%2).", parts[0], parts[1]);
        }
+       case VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED:
+               return "Some aspect of this DCP could not be checked because it is encrypted.";
        }
 
        return "";
@@ -1616,6 +1639,29 @@ dcp::operator== (dcp::VerificationNote const& a, dcp::VerificationNote const& b)
 }
 
 
+bool
+dcp::operator< (dcp::VerificationNote const& a, dcp::VerificationNote const& b)
+{
+       if (a.type() != b.type()) {
+               return a.type() < b.type();
+       }
+
+       if (a.code() != b.code()) {
+               return a.code() < b.code();
+       }
+
+       if (a.note() != b.note()) {
+               return a.note().get_value_or("") < b.note().get_value_or("");
+       }
+
+       if (a.file() != b.file()) {
+               return a.file().get_value_or("") < b.file().get_value_or("");
+       }
+
+       return a.line().get_value_or(0) < b.line().get_value_or(0);
+}
+
+
 std::ostream&
 dcp::operator<< (std::ostream& s, dcp::VerificationNote const& note)
 {