X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fverify.cc;h=c9d9b24d04e6081663490b77f6d67de1011c836c;hb=ba27603d5b53231607bc8fe41b201d8811b22b4f;hp=8c36756306511661ba757d7588245c5751c5924f;hpb=cf4e4272f72346c39964b128f78b2297f04dba55;p=libdcp.git diff --git a/src/verify.cc b/src/verify.cc index 8c367563..c9d9b24d 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -37,46 +37,49 @@ */ -#include "verify.h" -#include "dcp.h" +#include "compose.hpp" #include "cpl.h" +#include "dcp.h" +#include "exceptions.h" +#include "interop_subtitle_asset.h" +#include "mono_picture_asset.h" +#include "mono_picture_frame.h" +#include "raw_convert.h" #include "reel.h" #include "reel_closed_caption_asset.h" +#include "reel_interop_subtitle_asset.h" +#include "reel_markers_asset.h" #include "reel_picture_asset.h" #include "reel_sound_asset.h" +#include "reel_smpte_subtitle_asset.h" #include "reel_subtitle_asset.h" -#include "interop_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "mono_picture_frame.h" +#include "smpte_subtitle_asset.h" #include "stereo_picture_asset.h" #include "stereo_picture_frame.h" -#include "exceptions.h" -#include "compose.hpp" -#include "raw_convert.h" -#include "reel_markers_asset.h" -#include "smpte_subtitle_asset.h" -#include -#include -#include -#include +#include "verify.h" +#include "verify_j2k.h" +#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include #include #include -#include -#include +#include #include #include +#include +#include +#include +#include #include +#include #include #include -#include using std::list; @@ -153,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 (); } @@ -243,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; @@ -420,67 +423,60 @@ verify_language_tag (string tag, vector& notes) } -enum class VerifyPictureAssetResult -{ - GOOD, - FRAME_NEARLY_TOO_LARGE, - BAD, -}; - - -int -biggest_frame_size (shared_ptr frame) -{ - return frame->size (); -} - -int -biggest_frame_size (shared_ptr frame) +static void +verify_picture_asset (shared_ptr reel_file_asset, boost::filesystem::path file, vector& notes, function progress) { - return max(frame->left()->size(), frame->right()->size()); -} + int biggest_frame = 0; + auto asset = dynamic_pointer_cast(reel_file_asset->asset_ref().asset()); + auto const duration = asset->intrinsic_duration (); + auto check_and_add = [¬es](vector const& j2k_notes) { + for (auto i: j2k_notes) { + if (find(notes.begin(), notes.end(), i) == notes.end()) { + notes.push_back (i); + } + } + }; -template -optional -verify_picture_asset_type (shared_ptr reel_file_asset, function progress) -{ - auto asset = dynamic_pointer_cast(reel_file_asset->asset_ref().asset()); - if (!asset) { - return optional(); - } + if (auto mono_asset = dynamic_pointer_cast(reel_file_asset->asset_ref().asset())) { + auto reader = mono_asset->start_read (); + for (int64_t i = 0; i < duration; ++i) { + auto frame = reader->get_frame (i); + biggest_frame = max(biggest_frame, frame->size()); + if (!mono_asset->encrypted() || mono_asset->key()) { + vector j2k_notes; + verify_j2k (frame, j2k_notes); + check_and_add (j2k_notes); + } + progress (float(i) / duration); + } + } else if (auto stereo_asset = dynamic_pointer_cast(asset)) { + auto reader = stereo_asset->start_read (); + for (int64_t i = 0; i < duration; ++i) { + auto frame = reader->get_frame (i); + biggest_frame = max(biggest_frame, max(frame->left()->size(), frame->right()->size())); + if (!stereo_asset->encrypted() || mono_asset->key()) { + vector j2k_notes; + verify_j2k (frame->left(), j2k_notes); + verify_j2k (frame->right(), j2k_notes); + check_and_add (j2k_notes); + } + progress (float(i) / duration); + } - int biggest_frame = 0; - auto reader = asset->start_read (); - auto const duration = asset->intrinsic_duration (); - for (int64_t i = 0; i < duration; ++i) { - shared_ptr frame = reader->get_frame (i); - biggest_frame = max(biggest_frame, biggest_frame_size(frame)); - progress (float(i) / duration); } static const int max_frame = rint(250 * 1000000 / (8 * asset->edit_rate().as_float())); static const int risky_frame = rint(230 * 1000000 / (8 * asset->edit_rate().as_float())); if (biggest_frame > max_frame) { - return VerifyPictureAssetResult::BAD; + notes.push_back ({ + VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file + }); } else if (biggest_frame > risky_frame) { - return VerifyPictureAssetResult::FRAME_NEARLY_TOO_LARGE; - } - - return VerifyPictureAssetResult::GOOD; -} - - -static VerifyPictureAssetResult -verify_picture_asset (shared_ptr reel_file_asset, function progress) -{ - auto r = verify_picture_asset_type(reel_file_asset, progress); - if (!r) { - r = verify_picture_asset_type(reel_file_asset, progress); + notes.push_back ({ + VerificationNote::Type::WARNING, VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file + }); } - - DCP_ASSERT (r); - return *r; } @@ -512,21 +508,7 @@ verify_main_picture_asset ( break; } stage ("Checking picture frame sizes", asset->file()); - auto const pr = verify_picture_asset (reel_asset, progress); - switch (pr) { - case VerifyPictureAssetResult::BAD: - notes.push_back ({ - VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, file - }); - break; - 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; - } + verify_picture_asset (reel_asset, file, notes, progress); /* Only flat/scope allowed by Bv2.1 */ if ( @@ -606,7 +588,9 @@ verify_main_sound_asset ( stage ("Checking sound asset metadata", asset->file()); - verify_language_tag (asset->language(), notes); + if (auto lang = asset->language()) { + verify_language_tag (*lang, notes); + } if (asset->sampling_rate() != 48000) { notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_SOUND_FRAME_RATE, raw_convert(asset->sampling_rate()), *asset->file()}); } @@ -655,6 +639,7 @@ struct State void verify_smpte_timed_text_asset ( shared_ptr asset, + optional reel_asset_duration, vector& notes ) { @@ -688,6 +673,16 @@ verify_smpte_timed_text_asset ( } else if (asset->start_time() != Time()) { notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_SUBTITLE_START_TIME, asset->file().get() }); } + + if (reel_asset_duration && *reel_asset_duration != asset->intrinsic_duration()) { + notes.push_back ( + { + VerificationNote::Type::BV21_ERROR, + VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION, + String::compose("%1 %2", *reel_asset_duration, asset->intrinsic_duration()), + asset->file().get() + }); + } } @@ -706,6 +701,15 @@ verify_smpte_subtitle_asset ( notes.push_back ({ VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES }); } } + + 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 }); + } + + 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 }); + } } @@ -713,6 +717,7 @@ verify_smpte_subtitle_asset ( static void verify_subtitle_asset ( shared_ptr asset, + optional reel_asset_duration, function)> stage, boost::filesystem::path xsd_dtd_directory, vector& notes, @@ -727,7 +732,7 @@ verify_subtitle_asset ( auto smpte = dynamic_pointer_cast(asset); if (smpte) { - verify_smpte_timed_text_asset (smpte, notes); + verify_smpte_timed_text_asset (smpte, reel_asset_duration, notes); verify_smpte_subtitle_asset (smpte, notes, state); } } @@ -737,6 +742,7 @@ verify_subtitle_asset ( static void verify_closed_caption_asset ( shared_ptr asset, + optional reel_asset_duration, function)> stage, boost::filesystem::path xsd_dtd_directory, vector& notes @@ -750,7 +756,7 @@ verify_closed_caption_asset ( auto smpte = dynamic_pointer_cast(asset); if (smpte) { - verify_smpte_timed_text_asset (smpte, notes); + verify_smpte_timed_text_asset (smpte, reel_asset_duration, notes); } if (asset->raw_xml().size() > 256 * 1024) { @@ -987,7 +993,13 @@ verify_text_timing (vector> reels, vector& no return static_cast(reel->main_subtitle()); }, [](shared_ptr reel) { - return reel->main_subtitle()->asset()->raw_xml(); + auto interop = dynamic_pointer_cast(reel->main_subtitle()); + if (interop) { + return interop->asset()->raw_xml(); + } + auto smpte = dynamic_pointer_cast(reel->main_subtitle()); + DCP_ASSERT (smpte); + return smpte->asset()->raw_xml(); }, [](shared_ptr reel) { return reel->main_subtitle()->actual_duration(); @@ -1076,14 +1088,9 @@ pkl_has_encrypted_assets (shared_ptr dcp, shared_ptr pkl) for (auto i: dcp->cpls()) { 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 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()) { - encrypted.push_back(j->asset_ref().id()); - } + auto mxf = dynamic_pointer_cast(j->asset_ref().asset()); + if (mxf && mxf->encrypted()) { + encrypted.push_back(j->asset_ref().id()); } } } @@ -1122,8 +1129,12 @@ dcp::verify ( for (auto dcp: dcps) { stage ("Checking DCP", dcp->directory()); + bool carry_on = true; try { dcp->read (¬es); + } catch (MissingAssetmapError& e) { + notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); + carry_on = false; } catch (ReadError& e) { notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } catch (XMLError& e) { @@ -1134,6 +1145,10 @@ dcp::verify ( notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::FAILED_READ, string(e.what())}); } + if (!carry_on) { + continue; + } + if (dcp->standard() != Standard::SMPTE) { notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::INVALID_STANDARD}); } @@ -1222,7 +1237,7 @@ dcp::verify ( notes.push_back ({VerificationNote::Type::ERROR, VerificationNote::Code::INVALID_INTRINSIC_DURATION, i->id()}); } auto file_asset = dynamic_pointer_cast(i); - if (file_asset && !file_asset->hash()) { + if (i->encryptable() && !file_asset->hash()) { notes.push_back ({VerificationNote::Type::BV21_ERROR, VerificationNote::Code::MISSING_HASH, i->id()}); } } @@ -1269,7 +1284,7 @@ dcp::verify ( 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(), reel->main_subtitle()->duration(), stage, *xsd_dtd_directory, notes, state); } have_main_subtitle = true; } else { @@ -1279,7 +1294,7 @@ dcp::verify ( 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); + verify_closed_caption_asset (i->asset(), i->duration(), stage, *xsd_dtd_directory, notes); } } @@ -1577,6 +1592,17 @@ dcp::note_to_string (VerificationNote note) return String::compose("The JPEG2000 codestream has %1 tile parts in a 4K image instead of 6.", note.note().get()); case VerificationNote::Code::MISSING_JPEG200_TLM_MARKER: return "No TLM marker was found in a JPEG2000 codestream."; + case VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID: + return "The Resource ID in a timed text MXF did not match the ID of the contained XML."; + case VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID: + return "The Asset ID in a timed text MXF is the same as the Resource ID or that of the contained XML."; + case VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION: + { + vector parts; + boost::split (parts, note.note().get(), boost::is_any_of(" ")); + 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]); + } } return "";