2 Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 #include "compose.hpp"
39 #include "interop_subtitle_asset.h"
40 #include "j2k_transcode.h"
41 #include "mono_picture_asset.h"
42 #include "mono_picture_asset_writer.h"
43 #include "openjpeg_image.h"
44 #include "raw_convert.h"
46 #include "reel_interop_closed_caption_asset.h"
47 #include "reel_interop_subtitle_asset.h"
48 #include "reel_markers_asset.h"
49 #include "reel_mono_picture_asset.h"
50 #include "reel_sound_asset.h"
51 #include "reel_stereo_picture_asset.h"
52 #include "reel_smpte_closed_caption_asset.h"
53 #include "reel_smpte_subtitle_asset.h"
54 #include "smpte_subtitle_asset.h"
55 #include "stereo_picture_asset.h"
56 #include "stream_operators.h"
60 #include "verify_j2k.h"
61 #include <boost/algorithm/string.hpp>
62 #include <boost/random.hpp>
63 #include <boost/test/unit_test.hpp>
71 using std::make_shared;
73 using std::shared_ptr;
76 using boost::optional;
77 using namespace boost::filesystem;
80 static list<pair<string, optional<path>>> stages;
82 static string filename_to_id(boost::filesystem::path path)
84 return path.string().substr(4, path.string().length() - 8);
88 boost::filesystem::path
91 return find_file("test/ref/DCP/dcp_test1", "pkl_").filename();
98 return filename_to_id(dcp_test1_pkl());
102 boost::filesystem::path
105 return find_file("test/ref/DCP/dcp_test1", "cpl_").filename();
112 return filename_to_id(dcp_test1_cpl());
115 static string const dcp_test1_asset_map_id = "017b3de4-6dda-408d-b19b-6711354b0bc3";
119 encryption_test_cpl_id()
121 return filename_to_id(find_file("test/ref/DCP/encryption_test", "cpl_").filename());
126 encryption_test_pkl_id()
128 return filename_to_id(find_file("test/ref/DCP/encryption_test", "pkl_").filename());
132 stage (string s, optional<path> p)
134 stages.push_back (make_pair (s, p));
144 prepare_directory (path path)
146 using namespace boost::filesystem;
148 create_directories (path);
154 find_prefix(path dir, string prefix)
156 auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) {
157 return boost::starts_with(p.filename().string(), prefix);
160 BOOST_REQUIRE(iter != directory_iterator());
169 return find_prefix(dir, "cpl_");
177 return find_prefix(dir, "pkl_");
183 find_asset_map(path dir)
185 return find_prefix(dir, "ASSETMAP");
189 /** Copy dcp_test{reference_number} to build/test/verify_test{verify_test_suffix}
190 * to make a new sacrificial test DCP.
193 setup (int reference_number, string verify_test_suffix)
195 auto const dir = dcp::String::compose("build/test/verify_test%1", verify_test_suffix);
196 prepare_directory (dir);
197 for (auto i: directory_iterator(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number))) {
198 copy_file (i.path(), dir / i.path().filename());
207 write_dcp_with_single_asset (path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::Standard::SMPTE)
209 auto reel = make_shared<dcp::Reel>();
210 reel->add (reel_asset);
211 reel->add (simple_markers());
213 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, standard);
215 auto dcp = make_shared<dcp::DCP>(dir);
217 dcp->set_annotation_text("hello");
224 LIBDCP_DISABLE_WARNINGS
227 dump_notes (vector<dcp::VerificationNote> const & notes)
229 for (auto i: notes) {
230 std::cout << dcp::note_to_string(i) << "\n";
233 LIBDCP_ENABLE_WARNINGS
238 to_string(dcp::VerificationNote const& note)
240 string s = note_to_string(note) + dcp::String::compose(
241 "\n [%1 %2 %3 %4 %5 %6 ",
242 static_cast<int>(note.type()),
243 static_cast<int>(note.code()),
244 note.note().get_value_or("<none>"),
245 note.file().get_value_or("<none>"),
246 note.line().get_value_or(0),
247 note.frame().get_value_or(0)
250 s += dcp::String::compose(
252 note.id().get_value_or("<none>"),
253 note.other_id().get_value_or("<none>"),
254 note.cpl_id().get_value_or("<none>"),
255 note.reference_hash().get_value_or("<none>"),
256 note.calculated_hash().get_value_or("<none>")
265 check_verify_result(vector<dcp::VerificationNote> notes, vector<dcp::VerificationNote> test_notes)
267 std::sort(notes.begin(), notes.end());
268 std::sort(test_notes.begin(), test_notes.end());
270 string message = "\n";
272 vector<dcp::VerificationNote> not_expected;
273 for (auto note: notes) {
274 auto iter = std::find_if(test_notes.begin(), test_notes.end(), [note](dcp::VerificationNote const& n) { return note.type() == n.type() && note.code() == n.code(); });
275 if (iter != test_notes.end() && *iter != note) {
276 message += "Wrong details:\n --seen " + to_string(note) + " --expected " + to_string(*iter) + "\n";
277 } else if (iter == test_notes.end()) {
278 not_expected.push_back(note);
282 vector<dcp::VerificationNote> not_seen;
283 for (auto note: test_notes) {
284 auto iter = std::find_if(notes.begin(), notes.end(), [note](dcp::VerificationNote const& n) { return note.type() == n.type() && note.code() == n.code(); });
285 if (iter == notes.end()) {
286 not_seen.push_back(note);
290 for (auto note: not_expected) {
291 message += "Not expected:\n" + to_string(note) + "\n";
294 for (auto note: not_seen) {
295 message += "Not seen:\n" + to_string(note) + "\n";
298 BOOST_REQUIRE_MESSAGE(notes == test_notes, message);
304 check_verify_result(vector<path> dir, vector<dcp::DecryptedKDM> kdm, vector<dcp::VerificationNote> test_notes)
306 check_verify_result(dcp::verify({dir}, kdm, &stage, &progress, {}, xsd_test).notes, test_notes);
310 /* Copy dcp_test1 to build/test/verify_test{suffix} then edit a file found by the functor 'file',
311 * replacing from with to.
315 replace(string suffix, boost::function<path (string)> file, string from, string to)
317 auto dir = setup (1, suffix);
320 Editor e (file(suffix));
321 e.replace (from, to);
328 add_font(shared_ptr<dcp::SubtitleAsset> asset)
330 dcp::ArrayData fake_font(1024);
331 asset->add_font("font", fake_font);
338 HashCalculator(boost::filesystem::path path)
340 , _old_hash(dcp::make_digest(path, [](int64_t, int64_t) {}))
343 std::string old_hash() const {
347 std::string new_hash() const {
348 return dcp::make_digest(_path, [](int64_t, int64_t) {});
352 boost::filesystem::path _path;
353 std::string _old_hash;
358 dcp::VerificationNote
359 ok(dcp::VerificationNote::Code code, shared_ptr<const dcp::CPL> cpl)
361 return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code).set_cpl_id(cpl->id());
366 dcp::VerificationNote
367 ok(dcp::VerificationNote::Code code, boost::filesystem::path path, shared_ptr<const dcp::CPL> cpl)
369 return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code, path).set_cpl_id(cpl->id());
374 add(vector<dcp::VerificationNote>& notes, vector<dcp::VerificationNote> const& add)
382 BOOST_AUTO_TEST_CASE (verify_no_error)
385 auto dir = setup (1, "no_error");
386 auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes;
388 path const cpl_file = dir / dcp_test1_cpl();
389 path const pkl_file = dir / dcp_test1_pkl();
390 path const assetmap_file = dir / "ASSETMAP.xml";
392 auto st = stages.begin();
393 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
394 BOOST_REQUIRE (st->second);
395 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir));
397 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
398 BOOST_REQUIRE (st->second);
399 BOOST_CHECK_EQUAL (st->second.get(), canonical(cpl_file));
401 BOOST_CHECK_EQUAL (st->first, "Checking reel");
402 BOOST_REQUIRE (!st->second);
404 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
405 BOOST_REQUIRE (st->second);
406 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "video.mxf"));
408 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
409 BOOST_REQUIRE (st->second);
410 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "video.mxf"));
412 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
413 BOOST_REQUIRE (st->second);
414 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "audio.mxf"));
416 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
417 BOOST_REQUIRE (st->second);
418 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "audio.mxf"));
420 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
421 BOOST_REQUIRE (st->second);
422 BOOST_CHECK_EQUAL (st->second.get(), canonical(pkl_file));
423 ++st; BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
424 BOOST_REQUIRE (st->second);
425 BOOST_CHECK_EQUAL (st->second.get(), canonical(assetmap_file));
427 BOOST_REQUIRE (st == stages.end());
429 for (auto note: notes) {
430 BOOST_CHECK(note.type() == dcp::VerificationNote::Type::OK);
435 BOOST_AUTO_TEST_CASE (verify_incorrect_picture_sound_hash)
437 using namespace boost::filesystem;
439 auto dir = setup (1, "incorrect_picture_sound_hash");
440 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
442 auto video_path = path(dir / "video.mxf");
443 HashCalculator video_calc(video_path);
444 auto mod = fopen(video_path.string().c_str(), "r+b");
446 BOOST_REQUIRE_EQUAL(fseek(mod, -16, SEEK_END), 0);
448 BOOST_REQUIRE(fwrite(&x, sizeof(x), 1, mod) == 1);
451 auto audio_path = path(dir / "audio.mxf");
452 HashCalculator audio_calc(audio_path);
453 mod = fopen(audio_path.string().c_str(), "r+b");
455 BOOST_REQUIRE_EQUAL(fseek(mod, 0, SEEK_END), 0);
456 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
459 dcp::ASDCPErrorSuspender sus;
460 check_verify_result (
464 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
465 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
466 dcp::VerificationNote(
467 dcp::VerificationNote::Type::OK,
468 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
470 canonical(cpl->file().get())
471 ).set_cpl_id(dcp_test1_cpl_id()),
472 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
473 dcp::VerificationNote(
474 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_PICTURE_HASH, canonical(video_path)
475 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(video_calc.old_hash()).set_calculated_hash(video_calc.new_hash()),
476 dcp::VerificationNote(
477 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_SOUND_HASH, canonical(audio_path)
478 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(audio_calc.old_hash()).set_calculated_hash(audio_calc.new_hash()),
483 BOOST_AUTO_TEST_CASE (verify_mismatched_picture_sound_hashes)
485 using namespace boost::filesystem;
487 auto dir = setup (1, "mismatched_picture_sound_hashes");
488 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
490 HashCalculator calc(dir / dcp_test1_cpl());
493 Editor e (dir / dcp_test1_pkl());
494 e.replace ("<Hash>", "<Hash>x");
497 check_verify_result (
501 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
502 dcp::VerificationNote(
503 dcp::VerificationNote::Type::OK,
504 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
506 canonical(cpl->file().get())
507 ).set_cpl_id(cpl->id()),
508 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
509 dcp::VerificationNote(
510 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(dir / dcp_test1_cpl())
511 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash("x" + calc.old_hash()).set_calculated_hash(calc.old_hash()),
512 dcp::VerificationNote(
513 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES, canonical(dir / "video.mxf")
514 ).set_cpl_id(dcp_test1_cpl_id()),
515 dcp::VerificationNote(
516 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES, canonical(dir / "audio.mxf")
517 ).set_cpl_id(dcp_test1_cpl_id()),
518 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'x3M7YTgvFKXXMEGLkIbV4miC90FE=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 28 },
519 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xskI+5b/9LA/y6h0mcyxysJYanxI=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 12 },
520 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xvsVjRV9vhTBPUWfE/TT1o2vdQsI=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 20 },
525 BOOST_AUTO_TEST_CASE (verify_failed_read_content_kind)
527 auto dir = setup (1, "failed_read_content_kind");
529 HashCalculator calc(dir / dcp_test1_cpl());
532 Editor e (dir / dcp_test1_cpl());
533 e.replace ("<ContentKind>", "<ContentKind>x");
536 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
538 check_verify_result (
542 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
543 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
544 dcp::VerificationNote(
545 dcp::VerificationNote::Type::OK,
546 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
548 canonical(cpl->file().get())
549 ).set_cpl_id(cpl->id()),
550 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
551 dcp::VerificationNote(
552 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(dir / dcp_test1_cpl())
553 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
554 dcp::VerificationNote(
555 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("xtrailer")
556 ).set_cpl_id(dcp_test1_cpl_id())
563 dcp_test1_cpl_path(string suffix)
565 return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_cpl());
571 dcp_test1_pkl_path(string suffix)
573 return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_pkl());
579 asset_map (string suffix)
581 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", suffix);
585 BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_rate)
587 auto const suffix = "invalid_picture_frame_rate";
589 replace(suffix, &dcp_test1_cpl_path, "<FrameRate>24 1", "<FrameRate>99 1");
591 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
592 auto const cpl_path = find_cpl(dir);
593 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
595 std::vector<dcp::VerificationNote> expected =
597 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
598 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
599 dcp::VerificationNote(
600 dcp::VerificationNote::Type::OK,
601 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
603 canonical(cpl->file().get())
604 ).set_cpl_id(cpl->id()),
605 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
606 dcp::VerificationNote(
607 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
608 ).set_cpl_id(cpl->id()).set_calculated_hash("7n7GQ2TbxQbmHYuAR8ml7XDOep8=").set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI="),
609 dcp::VerificationNote(
610 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, string{"99/1"}
611 ).set_cpl_id(cpl->id())
614 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
617 BOOST_AUTO_TEST_CASE (verify_missing_asset)
619 auto dir = setup (1, "missing_asset");
620 remove (dir / "video.mxf");
622 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
624 check_verify_result (
628 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
629 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
630 dcp::VerificationNote(
631 dcp::VerificationNote::Type::OK,
632 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
634 canonical(cpl->file().get())
635 ).set_cpl_id(cpl->id()),
636 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_ASSET, canonical(dir) / "video.mxf" }
641 BOOST_AUTO_TEST_CASE (verify_empty_asset_path)
643 auto const suffix = "empty_asset_path";
645 replace("empty_asset_path", &asset_map, "<Path>video.mxf</Path>", "<Path></Path>");
647 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
648 auto const cpl_path = find_cpl(dir);
649 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
651 std::vector<dcp::VerificationNote> expected = {
652 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
653 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
654 dcp::VerificationNote(
655 dcp::VerificationNote::Type::OK,
656 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
658 canonical(cpl->file().get())
659 ).set_cpl_id(cpl->id()),
660 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_ASSET_PATH }
663 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
667 BOOST_AUTO_TEST_CASE (verify_mismatched_standard)
669 auto const suffix = "mismatched_standard";
671 replace(suffix, &dcp_test1_cpl_path, "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#");
673 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
674 auto const cpl_path = find_cpl(dir);
675 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
677 std::vector<dcp::VerificationNote> expected = {
678 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
679 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
680 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
681 dcp::VerificationNote(
682 dcp::VerificationNote::Type::OK,
683 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
685 canonical(cpl->file().get())
686 ).set_cpl_id(cpl->id()),
687 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_STANDARD },
688 dcp::VerificationNote(
689 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "invalid character encountered", canonical(cpl_path), 42
690 ).set_cpl_id(cpl->id()),
691 dcp::VerificationNote(
692 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'Id'", canonical(cpl_path), 53
693 ).set_cpl_id(cpl->id()),
694 dcp::VerificationNote(
695 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'EditRate'", canonical(cpl_path), 54
696 ).set_cpl_id(cpl->id()),
697 dcp::VerificationNote(
698 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'IntrinsicDuration'", canonical(cpl_path), 55
699 ).set_cpl_id(cpl->id()),
700 dcp::VerificationNote(
701 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
702 "element 'Id' is not allowed for content model '(Id,AnnotationText?,EditRate,IntrinsicDuration,"
703 "EntryPoint?,Duration?,FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
704 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,MainSoundSampleRate,"
705 "MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,ExtensionMetadataList?,)'",
706 canonical(cpl_path), 149
707 ).set_cpl_id(cpl->id()),
708 dcp::VerificationNote(
709 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
710 ).set_cpl_id(cpl->id()).set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI=").set_calculated_hash("FZ9E7L/pOuJ6aZfbiaANTv8BFOo=")
713 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
717 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_id)
719 auto const suffix = "invalid_xml_cpl_id";
721 /* There's no MISMATCHED_CPL_HASHES error here because it can't find the correct hash by ID (since the ID is wrong) */
722 replace("invalid_xml_cpl_id", &dcp_test1_cpl_path, "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab", "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a");
724 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
725 auto const cpl_path = find_cpl(dir);
726 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
728 std::vector<dcp::VerificationNote> expected = {
729 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
730 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
731 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
732 dcp::VerificationNote(
733 dcp::VerificationNote::Type::OK,
734 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
736 canonical(cpl->file().get())
737 ).set_cpl_id(cpl->id()),
738 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
739 dcp::VerificationNote(
740 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
741 "value 'urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a' does not match regular expression "
742 "facet 'urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'", canonical(cpl_path), 3
743 ).set_cpl_id(cpl->id())
746 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
750 BOOST_AUTO_TEST_CASE (verify_invalid_xml_issue_date)
752 auto const suffix = "invalid_xml_issue_date";
754 replace("invalid_xml_issue_date", &dcp_test1_cpl_path, "<IssueDate>", "<IssueDate>x");
756 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
757 auto const cpl_path = find_cpl(dir);
758 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
760 std::vector<dcp::VerificationNote> expected = {
761 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
762 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
763 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
764 dcp::VerificationNote(
765 dcp::VerificationNote::Type::OK,
766 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
768 canonical(cpl->file().get())
769 ).set_cpl_id(cpl->id()),
770 dcp::VerificationNote(
771 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
772 ).set_cpl_id(cpl->id()).set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI=").set_calculated_hash("sz3BeIugJ567q3HMnA62JeRw4TE="),
773 dcp::VerificationNote(
774 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
775 "invalid character encountered",
776 canonical(cpl_path), 5
777 ).set_cpl_id(cpl->id()),
780 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
784 BOOST_AUTO_TEST_CASE (verify_invalid_xml_pkl_id)
786 auto const suffix = "invalid_xml_pkl_id";
788 replace("invalid_xml_pkl_id", &dcp_test1_pkl_path, "<Id>urn:uuid:" + dcp_test1_pkl_id().substr(0, 3), "<Id>urn:uuid:x" + dcp_test1_pkl_id().substr(1, 2));
790 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
791 auto const pkl_path = find_pkl(dir);
792 auto const cpl_path = find_cpl(dir);
793 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
795 std::vector<dcp::VerificationNote> expected = {
796 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
797 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
798 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
799 dcp::VerificationNote(
800 dcp::VerificationNote::Type::OK,
801 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
803 canonical(cpl->file().get())
804 ).set_cpl_id(cpl->id()),
805 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
806 dcp::VerificationNote(
807 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
808 "value 'urn:uuid:x199d58b-5ef8-4d49-b270-07e590ccb280' does not match regular "
809 "expression facet 'urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'",
810 canonical(pkl_path), 3
814 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
818 BOOST_AUTO_TEST_CASE (verify_invalid_xml_asset_map_id)
820 auto const suffix = "invalid_xml_asset_map_id";
822 replace("invalid_xml_asset_map_id", &asset_map, "<Id>urn:uuid:" + dcp_test1_asset_map_id.substr(0, 3), "<Id>urn:uuid:x" + dcp_test1_asset_map_id.substr(1, 2));
824 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
825 auto const cpl_path = find_cpl(dir);
826 auto const asset_map_path = find_asset_map(dir);
827 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
829 std::vector<dcp::VerificationNote> expected = {
830 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
831 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
832 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
833 dcp::VerificationNote(
834 dcp::VerificationNote::Type::OK,
835 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
837 canonical(cpl->file().get())
838 ).set_cpl_id(cpl->id()),
839 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
840 dcp::VerificationNote(
841 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
842 "value 'urn:uuid:x17b3de4-6dda-408d-b19b-6711354b0bc3' does not match regular "
843 "expression facet 'urn:uuid:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'",
844 canonical(asset_map_path), 3
848 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
852 BOOST_AUTO_TEST_CASE (verify_invalid_standard)
855 auto dir = setup (3, "verify_invalid_standard");
856 auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes;
858 path const cpl_file = dir / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
859 path const pkl_file = dir / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
860 path const assetmap_file = dir / "ASSETMAP";
861 auto cpl = std::make_shared<dcp::CPL>(cpl_file);
863 auto st = stages.begin();
864 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
865 BOOST_REQUIRE (st->second);
866 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir));
868 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
869 BOOST_REQUIRE (st->second);
870 BOOST_CHECK_EQUAL (st->second.get(), canonical(cpl_file));
872 BOOST_CHECK_EQUAL (st->first, "Checking reel");
873 BOOST_REQUIRE (!st->second);
875 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
876 BOOST_REQUIRE (st->second);
877 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
879 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
880 BOOST_REQUIRE (st->second);
881 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
883 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
884 BOOST_REQUIRE (st->second);
885 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
887 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
888 BOOST_REQUIRE (st->second);
889 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
891 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
892 BOOST_REQUIRE (st->second);
893 BOOST_CHECK_EQUAL (st->second.get(), canonical(pkl_file));
895 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
896 BOOST_REQUIRE (st->second);
897 BOOST_CHECK_EQUAL (st->second.get(), canonical(assetmap_file));
899 BOOST_REQUIRE (st == stages.end());
901 vector<dcp::VerificationNote> expected = {
902 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
903 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
904 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
905 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"), cpl),
906 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"), cpl)
909 for (int j = 0; j < 24; ++j) {
911 dcp::VerificationNote(
912 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2")
913 ).set_cpl_id(cpl->id())
917 check_verify_result(notes, expected);
920 /* DCP with a short asset */
921 BOOST_AUTO_TEST_CASE (verify_invalid_duration)
923 auto dir = setup (8, "invalid_duration");
927 BOOST_REQUIRE(dcp.cpls().size() == 1);
928 auto cpl = dcp.cpls()[0];
930 vector<dcp::VerificationNote> expected = {
931 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
932 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
933 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_d7576dcb-a361-4139-96b8-267f5f8d7f91.mxf"), cpl),
934 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_d7576dcb-a361-4139-96b8-267f5f8d7f91.mxf"), cpl),
935 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
936 dcp::VerificationNote(
937 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91")
938 ).set_cpl_id(cpl->id()),
939 dcp::VerificationNote(
940 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91")
941 ).set_cpl_id(cpl->id()),
942 dcp::VerificationNote(
943 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626")
944 ).set_cpl_id(cpl->id()),
945 dcp::VerificationNote(
946 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626")
947 ).set_cpl_id(cpl->id()),
948 dcp::VerificationNote(
949 dcp::VerificationNote::Type::WARNING,
950 dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT,
952 ).set_cpl_id(cpl->id())
955 for (int i = 0; i < 23; ++i) {
957 dcp::VerificationNote(
958 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2")
959 ).set_cpl_id(cpl->id())
963 check_verify_result({ dir }, {}, expected);
969 dcp_from_frame (dcp::ArrayData const& frame, path dir)
971 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE);
972 create_directories (dir);
973 auto writer = asset->start_write(dir / "pic.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
974 for (int i = 0; i < 24; ++i) {
975 writer->write (frame.data(), frame.size());
979 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
980 return write_dcp_with_single_asset (dir, reel_asset);
984 BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_size_in_bytes)
986 int const too_big = 1302083 * 2;
988 /* Compress a black image */
989 auto image = black_image ();
990 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
991 BOOST_REQUIRE (frame.size() < too_big);
993 /* Place it in a bigger block with some zero padding at the end */
994 dcp::ArrayData oversized_frame(too_big);
995 memcpy (oversized_frame.data(), frame.data(), frame.size());
996 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
998 path const dir("build/test/verify_invalid_picture_frame_size_in_bytes");
999 prepare_directory (dir);
1000 auto cpl = dcp_from_frame (oversized_frame, dir);
1002 vector<dcp::VerificationNote> expected = {
1003 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1004 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1005 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1008 for (auto i = 0; i < 24; ++i) {
1010 dcp::VerificationNote(
1011 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte")
1012 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1016 for (auto i = 0; i < 24; ++i) {
1018 dcp::VerificationNote(
1019 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf")
1020 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1025 dcp::VerificationNote(
1026 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1027 ).set_cpl_id(cpl->id())
1030 check_verify_result({ dir }, {}, expected);
1034 BOOST_AUTO_TEST_CASE (verify_nearly_invalid_picture_frame_size_in_bytes)
1036 int const nearly_too_big = 1302083 * 0.98;
1038 /* Compress a black image */
1039 auto image = black_image ();
1040 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1041 BOOST_REQUIRE (frame.size() < nearly_too_big);
1043 /* Place it in a bigger block with some zero padding at the end */
1044 dcp::ArrayData oversized_frame(nearly_too_big);
1045 memcpy (oversized_frame.data(), frame.data(), frame.size());
1046 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
1048 path const dir("build/test/verify_nearly_invalid_picture_frame_size_in_bytes");
1049 prepare_directory (dir);
1050 auto cpl = dcp_from_frame (oversized_frame, dir);
1052 vector<dcp::VerificationNote> expected = {
1053 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1054 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1055 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1058 for (auto i = 0; i < 24; ++i) {
1060 dcp::VerificationNote(
1061 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte")
1062 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1066 for (auto i = 0; i < 24; ++i) {
1068 dcp::VerificationNote(
1069 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf")
1070 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1075 dcp::VerificationNote(
1076 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1077 ).set_cpl_id(cpl->id())
1080 check_verify_result ({ dir }, {}, expected);
1084 BOOST_AUTO_TEST_CASE (verify_valid_picture_frame_size_in_bytes)
1086 /* Compress a black image */
1087 auto image = black_image ();
1088 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1089 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
1091 path const dir("build/test/verify_valid_picture_frame_size_in_bytes");
1092 prepare_directory (dir);
1093 auto cpl = dcp_from_frame (frame, dir);
1095 check_verify_result(
1099 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1100 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1101 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1102 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "pic.mxf"), cpl),
1103 dcp::VerificationNote(dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id())
1108 BOOST_AUTO_TEST_CASE (verify_valid_interop_subtitles)
1110 path const dir("build/test/verify_valid_interop_subtitles");
1111 prepare_directory (dir);
1112 copy_file ("test/data/subs1.xml", dir / "subs.xml");
1113 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1114 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1115 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1117 check_verify_result (
1121 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1122 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1123 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1124 dcp::VerificationNote(
1125 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1126 ).set_cpl_id(cpl->id())
1131 BOOST_AUTO_TEST_CASE(verify_catch_missing_font_file_with_interop_ccap)
1133 path const dir("build/test/verify_catch_missing_font_file_with_interop_ccap");
1134 prepare_directory(dir);
1135 copy_file("test/data/subs1.xml", dir / "ccap.xml");
1136 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "ccap.xml");
1137 auto reel_asset = make_shared<dcp::ReelInteropClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1138 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1140 check_verify_result (
1144 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1145 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1146 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1147 dcp::VerificationNote(
1148 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1149 ).set_cpl_id(cpl->id())
1154 BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles)
1156 using namespace boost::filesystem;
1158 path const dir("build/test/verify_invalid_interop_subtitles");
1159 prepare_directory (dir);
1160 copy_file ("test/data/subs1.xml", dir / "subs.xml");
1161 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1162 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1163 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1166 Editor e (dir / "subs.xml");
1167 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
1170 check_verify_result (
1174 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1175 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1176 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1177 dcp::VerificationNote(
1178 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 5
1179 ).set_cpl_id(cpl->id()),
1180 dcp::VerificationNote(
1181 dcp::VerificationNote::Type::ERROR,
1182 dcp::VerificationNote::Code::INVALID_XML,
1183 string("element 'Foo' is not allowed for content model '(SubtitleID,MovieTitle,ReelNumber,Language,LoadFont*,Font*,Subtitle*)'"),
1186 ).set_cpl_id(cpl->id()),
1187 dcp::VerificationNote(
1188 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1189 ).set_cpl_id(cpl->id())
1194 BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_no_subtitles)
1196 path const dir("build/test/verify_interop_subtitle_asset_with_no_subtitles");
1197 prepare_directory(dir);
1198 copy_file("test/data/subs4.xml", dir / "subs.xml");
1199 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1200 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1201 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1203 check_verify_result (
1207 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1208 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1209 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1210 dcp::VerificationNote(
1211 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get())
1212 ).set_cpl_id(cpl->id()),
1213 dcp::VerificationNote(
1214 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1215 ).set_cpl_id(cpl->id())
1221 BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_single_space_subtitle)
1223 path const dir("build/test/verify_interop_subtitle_asset_with_single_space_subtitle");
1224 prepare_directory(dir);
1225 copy_file("test/data/subs5.xml", dir / "subs.xml");
1226 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1227 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1228 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1230 check_verify_result (
1234 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1235 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1236 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1237 dcp::VerificationNote(
1238 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"Arial"}
1239 ).set_cpl_id(cpl->id())
1245 BOOST_AUTO_TEST_CASE (verify_valid_smpte_subtitles)
1247 path const dir("build/test/verify_valid_smpte_subtitles");
1248 prepare_directory (dir);
1249 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1250 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1251 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1252 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1254 check_verify_result(
1258 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1259 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1260 dcp::VerificationNote(
1261 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1262 ).set_cpl_id(cpl->id()),
1263 dcp::VerificationNote(
1264 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-04-14T13:19:14.000+02:00"}
1265 ).set_cpl_id(cpl->id()),
1266 dcp::VerificationNote(
1267 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()
1268 ).set_cpl_id(cpl->id()),
1273 BOOST_AUTO_TEST_CASE (verify_invalid_smpte_subtitles)
1275 using namespace boost::filesystem;
1277 path const dir("build/test/verify_invalid_smpte_subtitles");
1278 prepare_directory (dir);
1279 /* This broken_smpte.mxf does not use urn:uuid: for its subtitle ID, which we tolerate (rightly or wrongly) */
1280 copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
1281 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1282 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1283 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1285 check_verify_result (
1289 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1290 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1291 dcp::VerificationNote(
1292 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 2
1293 ).set_cpl_id(cpl->id()),
1294 dcp::VerificationNote(
1295 dcp::VerificationNote::Type::ERROR,
1296 dcp::VerificationNote::Code::INVALID_XML,
1297 string("element 'Foo' is not allowed for content model '(Id,ContentTitleText,AnnotationText?,IssueDate,ReelNumber?,Language?,EditRate,TimeCodeRate,StartTime?,DisplayType?,LoadFont*,SubtitleList)'"),
1300 ).set_cpl_id(cpl->id()),
1301 dcp::VerificationNote(
1302 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
1303 ).set_cpl_id(cpl->id()),
1304 dcp::VerificationNote(
1305 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1306 ).set_cpl_id(cpl->id()),
1307 dcp::VerificationNote(
1308 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2020-05-09T00:29:21.000+02:00"}
1309 ).set_cpl_id(cpl->id()),
1310 dcp::VerificationNote(
1311 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()
1312 ).set_cpl_id(cpl->id()),
1317 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles)
1319 path const dir("build/test/verify_empty_text_node_in_subtitles");
1320 prepare_directory (dir);
1321 copy_file ("test/data/empty_text.mxf", dir / "subs.mxf");
1322 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1323 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
1324 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1326 check_verify_result (
1330 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1331 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1332 dcp::VerificationNote(
1333 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT
1334 ).set_cpl_id(cpl->id()),
1335 dcp::VerificationNote(
1336 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
1337 ).set_cpl_id(cpl->id()),
1338 dcp::VerificationNote(
1339 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf")
1340 ).set_cpl_id(cpl->id()),
1341 dcp::VerificationNote(
1342 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1343 ).set_cpl_id(cpl->id()),
1344 dcp::VerificationNote(
1345 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"}
1346 ).set_cpl_id(cpl->id()),
1347 dcp::VerificationNote(
1348 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()
1349 ).set_cpl_id(cpl->id())
1354 /** A <Text> node with no content except some <Font> nodes, which themselves do have content */
1355 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes)
1357 path const dir("build/test/verify_empty_text_node_in_subtitles_with_child_nodes");
1358 prepare_directory (dir);
1359 copy_file ("test/data/empty_but_with_children.xml", dir / "subs.xml");
1360 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1361 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
1362 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
1364 check_verify_result (
1368 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1369 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1370 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1371 dcp::VerificationNote(
1372 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"}
1373 ).set_cpl_id(cpl->id())
1378 /** A <Text> node with no content except some <Font> nodes, which themselves also have no content */
1379 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes)
1381 path const dir("build/test/verify_empty_text_node_in_subtitles_with_empty_child_nodes");
1382 prepare_directory (dir);
1383 copy_file ("test/data/empty_with_empty_children.xml", dir / "subs.xml");
1384 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1385 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
1386 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
1388 check_verify_result (
1392 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1393 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1394 dcp::VerificationNote(
1395 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get())
1396 ).set_cpl_id(cpl->id()),
1397 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1398 dcp::VerificationNote(
1399 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT
1400 ).set_cpl_id(cpl->id()),
1401 dcp::VerificationNote(
1402 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"}
1403 ).set_cpl_id(cpl->id())
1408 BOOST_AUTO_TEST_CASE (verify_external_asset)
1410 path const ov_dir("build/test/verify_external_asset");
1411 prepare_directory (ov_dir);
1413 auto image = black_image ();
1414 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1415 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
1416 dcp_from_frame (frame, ov_dir);
1418 dcp::DCP ov (ov_dir);
1421 path const vf_dir("build/test/verify_external_asset_vf");
1422 prepare_directory (vf_dir);
1424 auto picture = ov.cpls()[0]->reels()[0]->main_picture();
1425 auto cpl = write_dcp_with_single_asset (vf_dir, picture);
1427 check_verify_result (
1431 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1432 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1433 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, picture->asset()->id() },
1434 dcp::VerificationNote(
1435 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1436 ).set_cpl_id(cpl->id())
1441 BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata)
1443 path const dir("build/test/verify_valid_cpl_metadata");
1444 prepare_directory (dir);
1446 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1447 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1448 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1450 auto reel = make_shared<dcp::Reel>();
1451 reel->add (reel_asset);
1453 reel->add (make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 16 * 24), 0));
1454 reel->add (simple_markers(16 * 24));
1456 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1458 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1459 cpl->set_main_sound_sample_rate (48000);
1460 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1461 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1462 cpl->set_version_number (1);
1466 dcp.set_annotation_text("hello");
1471 /* DCP with invalid CompositionMetadataAsset */
1472 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag)
1474 using namespace boost::filesystem;
1476 path const dir("build/test/verify_invalid_cpl_metadata_bad_tag");
1477 prepare_directory (dir);
1479 auto reel = make_shared<dcp::Reel>();
1480 reel->add (black_picture_asset(dir));
1481 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1483 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1484 cpl->set_main_sound_sample_rate (48000);
1485 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1486 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1487 cpl->set_version_number (1);
1489 reel->add (simple_markers());
1493 dcp.set_annotation_text("hello");
1496 HashCalculator calc(find_cpl(dir));
1499 Editor e (find_cpl(dir));
1500 e.replace ("MainSound", "MainSoundX");
1503 check_verify_result (
1507 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1508 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1509 dcp::VerificationNote(
1510 dcp::VerificationNote::Type::OK,
1511 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1512 string{"1440x1080"},
1514 ).set_cpl_id(cpl->id()),
1515 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "pic.mxf"), cpl),
1516 dcp::VerificationNote(
1517 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50
1518 ).set_cpl_id(cpl->id()),
1519 dcp::VerificationNote(
1520 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51
1521 ).set_cpl_id(cpl->id()),
1522 dcp::VerificationNote(
1523 dcp::VerificationNote::Type::ERROR,
1524 dcp::VerificationNote::Code::INVALID_XML,
1525 string("element 'meta:MainSoundXConfiguration' is not allowed for content model "
1526 "'(Id,AnnotationText?,EditRate,IntrinsicDuration,EntryPoint?,Duration?,"
1527 "FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
1528 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,"
1529 "MainSoundSampleRate,MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,"
1530 "ExtensionMetadataList?,)'"),
1531 canonical(cpl->file().get()),
1532 71).set_cpl_id(cpl->id()),
1533 dcp::VerificationNote(
1534 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get())
1535 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash())
1540 /* DCP with invalid CompositionMetadataAsset */
1541 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_missing_tag)
1543 path const dir("build/test/verify_invalid_cpl_metadata_missing_tag");
1544 prepare_directory (dir);
1546 auto reel = make_shared<dcp::Reel>();
1547 reel->add (black_picture_asset(dir));
1548 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1550 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1551 cpl->set_main_sound_sample_rate (48000);
1552 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1553 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1557 dcp.set_annotation_text("hello");
1561 Editor e (find_cpl(dir));
1562 e.replace ("meta:Width", "meta:WidthX");
1565 check_verify_result (
1568 {{ dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::FAILED_READ, string("missing XML tag Width in MainPictureStoredArea") }}
1573 BOOST_AUTO_TEST_CASE (verify_invalid_language1)
1575 path const dir("build/test/verify_invalid_language1");
1576 prepare_directory (dir);
1577 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1578 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1579 asset->_language = "wrong-andbad";
1580 asset->write (dir / "subs.mxf");
1581 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1582 reel_asset->_language = "badlang";
1583 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1585 check_verify_result (
1589 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1590 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1591 dcp::VerificationNote(
1592 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang")
1593 ).set_cpl_id(cpl->id()),
1594 dcp::VerificationNote(
1595 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad")
1596 ).set_cpl_id(cpl->id()),
1597 dcp::VerificationNote(
1598 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1599 ).set_cpl_id(cpl->id())
1604 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
1605 BOOST_AUTO_TEST_CASE (verify_invalid_language2)
1607 path const dir("build/test/verify_invalid_language2");
1608 prepare_directory (dir);
1609 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1610 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1611 asset->_language = "wrong-andbad";
1612 asset->write (dir / "subs.mxf");
1613 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1614 reel_asset->_language = "badlang";
1615 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1617 check_verify_result (
1621 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1622 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1623 dcp::VerificationNote(
1624 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang")
1625 ).set_cpl_id(cpl->id()),
1626 dcp::VerificationNote(
1627 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad")
1628 ).set_cpl_id(cpl->id()),
1629 dcp::VerificationNote(
1630 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1631 ).set_cpl_id(cpl->id())
1636 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
1637 * the release territory.
1639 BOOST_AUTO_TEST_CASE (verify_invalid_language3)
1641 path const dir("build/test/verify_invalid_language3");
1642 prepare_directory (dir);
1644 auto picture = simple_picture (dir, "foo");
1645 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1646 auto reel = make_shared<dcp::Reel>();
1647 reel->add (reel_picture);
1648 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
1649 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1650 reel->add (reel_sound);
1651 reel->add (simple_markers());
1653 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1655 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
1656 cpl->_additional_subtitle_languages.push_back("andso-is-this");
1657 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1658 cpl->set_main_sound_sample_rate (48000);
1659 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1660 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1661 cpl->set_version_number (1);
1662 cpl->_release_territory = "fred-jim";
1663 auto dcp = make_shared<dcp::DCP>(dir);
1665 dcp->set_annotation_text("hello");
1668 check_verify_result (
1672 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "videofoo.mxf"), cpl),
1673 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1674 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1675 dcp::VerificationNote(
1676 dcp::VerificationNote::Type::OK,
1677 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1678 string{"1440x1080"},
1680 ).set_cpl_id(cpl->id()),
1681 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "videofoo.mxf"), cpl),
1682 dcp::VerificationNote(
1683 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong")
1684 ).set_cpl_id(cpl->id()),
1685 dcp::VerificationNote(
1686 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this")
1687 ).set_cpl_id(cpl->id()),
1688 dcp::VerificationNote(
1689 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim")
1690 ).set_cpl_id(cpl->id()),
1691 dcp::VerificationNote(
1692 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz")
1693 ).set_cpl_id(cpl->id()),
1699 std::tuple<vector<dcp::VerificationNote>, shared_ptr<dcp::CPL>, boost::filesystem::path>
1700 check_picture_size (int width, int height, int frame_rate, bool three_d)
1702 using namespace boost::filesystem;
1704 path dcp_path = "build/test/verify_picture_test";
1705 prepare_directory (dcp_path);
1707 shared_ptr<dcp::PictureAsset> mp;
1709 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1711 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1713 auto picture_writer = mp->start_write(dcp_path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
1715 auto image = black_image (dcp::Size(width, height));
1716 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1717 int const length = three_d ? frame_rate * 2 : frame_rate;
1718 for (int i = 0; i < length; ++i) {
1719 picture_writer->write (j2c.data(), j2c.size());
1721 picture_writer->finalize ();
1723 auto d = make_shared<dcp::DCP>(dcp_path);
1724 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1725 cpl->set_annotation_text ("A Test DCP");
1726 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1727 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1728 cpl->set_main_sound_sample_rate (48000);
1729 cpl->set_main_picture_stored_area(dcp::Size(width, height));
1730 cpl->set_main_picture_active_area(dcp::Size(width, height));
1731 cpl->set_version_number (1);
1733 auto reel = make_shared<dcp::Reel>();
1736 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1738 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1741 reel->add (simple_markers(frame_rate));
1746 d->set_annotation_text("A Test DCP");
1749 /* It seems that for the Ubuntu 16.04 compiler we can't use an initializer list here */
1750 return std::tuple<vector<dcp::VerificationNote>, shared_ptr<dcp::CPL>, boost::filesystem::path>{ dcp::verify({dcp_path}, {}, &stage, &progress, {}, xsd_test).notes, cpl, dcp_path };
1756 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1758 vector<dcp::VerificationNote> notes;
1759 shared_ptr<dcp::CPL> cpl;
1760 boost::filesystem::path dir;
1761 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1763 std::vector<dcp::VerificationNote> expected = {
1764 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1765 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1766 dcp::VerificationNote(
1767 dcp::VerificationNote::Type::OK,
1768 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1769 dcp::String::compose("%1x%2", width, height),
1771 ).set_cpl_id(cpl->id()),
1772 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1773 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl)
1775 check_verify_result(notes, expected);
1781 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1783 vector<dcp::VerificationNote> notes;
1784 shared_ptr<dcp::CPL> cpl;
1785 boost::filesystem::path dir;
1786 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1788 std::vector<dcp::VerificationNote> expected = {
1789 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1790 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1791 dcp::VerificationNote(
1792 dcp::VerificationNote::Type::OK,
1793 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1794 dcp::String::compose("%1x%2", width, height),
1796 ).set_cpl_id(cpl->id()),
1797 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1798 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1799 dcp::VerificationNote(
1800 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, dcp::String::compose("%1x%2", width, height), canonical(dir / "video.mxf")
1801 ).set_cpl_id(cpl->id())
1803 check_verify_result(notes, expected);
1809 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1811 vector<dcp::VerificationNote> notes;
1812 shared_ptr<dcp::CPL> cpl;
1813 boost::filesystem::path dir;
1814 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1816 std::vector<dcp::VerificationNote> expected = {
1817 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1818 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1819 dcp::VerificationNote(
1820 dcp::VerificationNote::Type::OK,
1821 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1822 dcp::String::compose("%1x%2", width, height),
1824 ).set_cpl_id(cpl->id()),
1825 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1826 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1827 dcp::VerificationNote(
1828 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, dcp::String::compose("%1/1", frame_rate * (three_d ? 2 : 1))
1829 ).set_cpl_id(cpl->id()),
1830 dcp::VerificationNote(
1831 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K, dcp::String::compose("%1/1", frame_rate), canonical(dir / "video.mxf")
1832 ).set_cpl_id(cpl->id())
1835 check_verify_result(notes, expected);
1841 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1843 vector<dcp::VerificationNote> notes;
1844 shared_ptr<dcp::CPL> cpl;
1845 boost::filesystem::path dir;
1846 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1848 std::vector<dcp::VerificationNote> expected = {
1849 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1850 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1851 dcp::VerificationNote(
1852 dcp::VerificationNote::Type::OK,
1853 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1854 dcp::String::compose("%1x%2", width, height),
1856 ).set_cpl_id(cpl->id()),
1857 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1858 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1859 dcp::VerificationNote(
1860 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K, dcp::String::compose("%1/1", frame_rate), canonical(dir / "video.mxf")
1861 ).set_cpl_id(cpl->id())
1864 check_verify_result(notes, expected);
1868 BOOST_AUTO_TEST_CASE (verify_picture_size)
1870 using namespace boost::filesystem;
1873 check_picture_size_ok (2048, 858, 24, false);
1874 check_picture_size_ok (2048, 858, 25, false);
1875 check_picture_size_ok (2048, 858, 48, false);
1876 check_picture_size_ok (2048, 858, 24, true);
1877 check_picture_size_ok (2048, 858, 25, true);
1878 check_picture_size_ok (2048, 858, 48, true);
1881 check_picture_size_ok (1998, 1080, 24, false);
1882 check_picture_size_ok (1998, 1080, 25, false);
1883 check_picture_size_ok (1998, 1080, 48, false);
1884 check_picture_size_ok (1998, 1080, 24, true);
1885 check_picture_size_ok (1998, 1080, 25, true);
1886 check_picture_size_ok (1998, 1080, 48, true);
1889 check_picture_size_ok (4096, 1716, 24, false);
1892 check_picture_size_ok (3996, 2160, 24, false);
1894 /* Bad frame size */
1895 check_picture_size_bad_frame_size (2050, 858, 24, false);
1896 check_picture_size_bad_frame_size (2048, 658, 25, false);
1897 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1898 check_picture_size_bad_frame_size (4000, 2000, 24, true);
1900 /* Bad 2K frame rate */
1901 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1902 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1903 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1905 /* Bad 4K frame rate */
1906 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1907 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1910 vector<dcp::VerificationNote> notes;
1911 shared_ptr<dcp::CPL> cpl;
1912 boost::filesystem::path dir;
1913 std::tie(notes, cpl, dir) = check_picture_size(3996, 2160, 24, true);
1915 std::vector<dcp::VerificationNote> expected = {
1916 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1917 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1918 dcp::VerificationNote(
1919 dcp::VerificationNote::Type::OK,
1920 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1921 string{"3996x2160"},
1923 ).set_cpl_id(cpl->id()),
1924 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1925 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1926 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D },
1933 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, dcp::VAlign v_align = dcp::VAlign::CENTER, string text = "Hello")
1936 std::make_shared<dcp::SubtitleString>(
1944 dcp::Time(start_frame, 24, 24),
1945 dcp::Time(end_frame, 24, 24),
1947 dcp::HAlign::CENTER,
1951 dcp::Direction::LTR,
1958 std::vector<dcp::Ruby>()
1964 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes)
1966 path const dir("build/test/verify_invalid_closed_caption_xml_size_in_bytes");
1967 prepare_directory (dir);
1969 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1970 for (int i = 0; i < 2048; ++i) {
1971 add_test_subtitle (asset, i * 24, i * 24 + 20);
1974 asset->set_language (dcp::LanguageTag("de-DE"));
1975 asset->write (dir / "subs.mxf");
1976 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 49148, 0);
1977 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1979 check_verify_result (
1983 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1984 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1985 dcp::VerificationNote(
1986 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
1987 ).set_cpl_id(cpl->id()),
1988 dcp::VerificationNote(
1989 dcp::VerificationNote::Type::BV21_ERROR,
1990 dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES,
1992 canonical(dir / "subs.mxf")
1993 ).set_cpl_id(cpl->id()),
1994 dcp::VerificationNote(
1995 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
1996 ).set_cpl_id(cpl->id()),
1997 dcp::VerificationNote(
1998 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1999 ).set_cpl_id(cpl->id())
2005 shared_ptr<dcp::SMPTESubtitleAsset>
2006 make_large_subtitle_asset (path font_file)
2008 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2009 dcp::ArrayData big_fake_font(1024 * 1024);
2010 big_fake_font.write (font_file);
2011 for (int i = 0; i < 116; ++i) {
2012 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
2020 verify_timed_text_asset_too_large (string name)
2022 auto const dir = path("build/test") / name;
2023 prepare_directory (dir);
2024 auto asset = make_large_subtitle_asset (dir / "font.ttf");
2025 add_test_subtitle (asset, 0, 240);
2026 asset->set_language (dcp::LanguageTag("de-DE"));
2027 asset->write (dir / "subs.mxf");
2029 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 240, 0);
2030 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
2032 check_verify_result (
2036 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2037 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2038 dcp::VerificationNote(
2039 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121698284"), canonical(dir / "subs.mxf")
2040 ).set_cpl_id(cpl->id()),
2041 dcp::VerificationNote(
2042 dcp::VerificationNote::Type::BV21_ERROR,
2043 dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES,
2044 dcp::raw_convert<string>(121634816),
2045 canonical(dir / "subs.mxf")
2046 ).set_cpl_id(cpl->id()),
2047 dcp::VerificationNote(
2048 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2049 ).set_cpl_id(cpl->id()),
2050 dcp::VerificationNote(
2051 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2052 ).set_cpl_id(cpl->id()),
2053 dcp::VerificationNote(
2054 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2055 ).set_cpl_id(cpl->id())
2060 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
2062 verify_timed_text_asset_too_large<dcp::ReelSMPTESubtitleAsset>("verify_subtitle_asset_too_large");
2063 verify_timed_text_asset_too_large<dcp::ReelSMPTEClosedCaptionAsset>("verify_closed_caption_asset_too_large");
2067 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language)
2069 path dir = "build/test/verify_missing_subtitle_language";
2070 prepare_directory (dir);
2071 auto dcp = make_simple (dir, 1, 106);
2074 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
2075 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
2076 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
2077 "<ContentTitleText>Content</ContentTitleText>"
2078 "<AnnotationText>Annotation</AnnotationText>"
2079 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
2080 "<ReelNumber>1</ReelNumber>"
2081 "<EditRate>24 1</EditRate>"
2082 "<TimeCodeRate>24</TimeCodeRate>"
2083 "<StartTime>00:00:00:00</StartTime>"
2084 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
2086 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
2087 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
2088 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
2094 dcp::File xml_file(dir / "subs.xml", "w");
2095 BOOST_REQUIRE (xml_file);
2096 xml_file.write(xml.c_str(), xml.size(), 1);
2098 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
2099 subs->write (dir / "subs.mxf");
2101 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
2102 auto cpl = dcp->cpls()[0];
2103 cpl->reels()[0]->add(reel_subs);
2106 check_verify_result (
2110 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2111 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2112 dcp::VerificationNote(
2113 dcp::VerificationNote::Type::OK,
2114 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2115 string{"1998x1080"},
2117 ).set_cpl_id(cpl->id()),
2118 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
2119 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
2120 dcp::VerificationNote(
2121 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf")
2122 ).set_cpl_id(cpl->id()),
2123 dcp::VerificationNote(
2124 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2125 ).set_cpl_id(cpl->id())
2130 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages)
2132 path path ("build/test/verify_mismatched_subtitle_languages");
2133 auto constexpr reel_length = 192;
2134 auto dcp = make_simple (path, 2, reel_length);
2135 auto cpl = dcp->cpls()[0];
2138 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2139 subs->set_language (dcp::LanguageTag("de-DE"));
2140 subs->add (simple_subtitle());
2142 subs->write (path / "subs1.mxf");
2143 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2144 cpl->reels()[0]->add(reel_subs);
2148 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2149 subs->set_language (dcp::LanguageTag("en-US"));
2150 subs->add (simple_subtitle());
2152 subs->write (path / "subs2.mxf");
2153 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2154 cpl->reels()[1]->add(reel_subs);
2159 check_verify_result (
2163 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video0.mxf"), cpl),
2164 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
2165 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2166 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2167 dcp::VerificationNote(
2168 dcp::VerificationNote::Type::OK,
2169 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2170 string{"1998x1080"},
2172 ).set_cpl_id(cpl->id()),
2173 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video0.mxf"), cpl),
2174 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
2175 dcp::VerificationNote(
2176 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf")
2177 ).set_cpl_id(cpl->id()),
2178 dcp::VerificationNote(
2179 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf")
2180 ).set_cpl_id(cpl->id()),
2181 dcp::VerificationNote(
2182 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES
2183 ).set_cpl_id(cpl->id()),
2188 BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed)
2190 path path ("build/test/verify_multiple_closed_caption_languages_allowed");
2191 auto constexpr reel_length = 192;
2192 auto dcp = make_simple (path, 2, reel_length);
2193 auto cpl = dcp->cpls()[0];
2196 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
2197 ccaps->set_language (dcp::LanguageTag("de-DE"));
2198 ccaps->add (simple_subtitle());
2200 ccaps->write (path / "subs1.mxf");
2201 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
2202 cpl->reels()[0]->add(reel_ccaps);
2206 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
2207 ccaps->set_language (dcp::LanguageTag("en-US"));
2208 ccaps->add (simple_subtitle());
2210 ccaps->write (path / "subs2.mxf");
2211 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
2212 cpl->reels()[1]->add(reel_ccaps);
2217 check_verify_result (
2221 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2222 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2223 dcp::VerificationNote(
2224 dcp::VerificationNote::Type::OK,
2225 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2226 string{"1998x1080"},
2228 ).set_cpl_id(cpl->id()),
2229 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video0.mxf"), cpl),
2230 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
2231 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video0.mxf"), cpl),
2232 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
2233 dcp::VerificationNote(
2234 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf")
2235 ).set_cpl_id(cpl->id()),
2236 dcp::VerificationNote(
2237 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf")
2238 ).set_cpl_id(cpl->id())
2243 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time)
2245 path dir = "build/test/verify_missing_subtitle_start_time";
2246 prepare_directory (dir);
2247 auto dcp = make_simple (dir, 1, 106);
2250 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
2251 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
2252 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
2253 "<ContentTitleText>Content</ContentTitleText>"
2254 "<AnnotationText>Annotation</AnnotationText>"
2255 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
2256 "<ReelNumber>1</ReelNumber>"
2257 "<Language>de-DE</Language>"
2258 "<EditRate>24 1</EditRate>"
2259 "<TimeCodeRate>24</TimeCodeRate>"
2260 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
2262 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
2263 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
2264 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
2270 dcp::File xml_file(dir / "subs.xml", "w");
2271 BOOST_REQUIRE (xml_file);
2272 xml_file.write(xml.c_str(), xml.size(), 1);
2274 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
2275 subs->write (dir / "subs.mxf");
2277 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
2278 auto cpl = dcp->cpls()[0];
2279 cpl->reels()[0]->add(reel_subs);
2282 check_verify_result (
2286 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
2287 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2288 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2289 dcp::VerificationNote(
2290 dcp::VerificationNote::Type::OK,
2291 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2292 string{"1998x1080"},
2294 ).set_cpl_id(cpl->id()),
2295 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
2296 dcp::VerificationNote(
2297 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2298 ).set_cpl_id(cpl->id()),
2299 dcp::VerificationNote(
2300 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2301 ).set_cpl_id(cpl->id())
2306 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time)
2308 path dir = "build/test/verify_invalid_subtitle_start_time";
2309 prepare_directory (dir);
2310 auto dcp = make_simple (dir, 1, 106);
2313 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
2314 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
2315 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
2316 "<ContentTitleText>Content</ContentTitleText>"
2317 "<AnnotationText>Annotation</AnnotationText>"
2318 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
2319 "<ReelNumber>1</ReelNumber>"
2320 "<Language>de-DE</Language>"
2321 "<EditRate>24 1</EditRate>"
2322 "<TimeCodeRate>24</TimeCodeRate>"
2323 "<StartTime>00:00:02:00</StartTime>"
2324 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
2326 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
2327 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
2328 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
2334 dcp::File xml_file(dir / "subs.xml", "w");
2335 BOOST_REQUIRE (xml_file);
2336 xml_file.write(xml.c_str(), xml.size(), 1);
2338 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
2339 subs->write (dir / "subs.mxf");
2341 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
2342 auto cpl = dcp->cpls()[0];
2343 cpl->reels().front()->add(reel_subs);
2346 check_verify_result (
2350 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2351 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2352 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
2353 dcp::VerificationNote(
2354 dcp::VerificationNote::Type::OK,
2355 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2356 string{"1998x1080"},
2358 ).set_cpl_id(cpl->id()),
2359 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
2360 dcp::VerificationNote(
2361 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2362 ).set_cpl_id(cpl->id()),
2363 dcp::VerificationNote(
2364 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2365 ).set_cpl_id(cpl->id())
2373 TestText (int in_, int out_, float v_position_ = 0, dcp::VAlign v_align_ = dcp::VAlign::CENTER, string text_ = "Hello")
2376 , v_position(v_position_)
2384 dcp::VAlign v_align;
2390 shared_ptr<dcp::CPL>
2391 dcp_with_text(path dir, vector<TestText> subs, optional<dcp::Key> key = boost::none, optional<string> key_id = boost::none)
2393 prepare_directory (dir);
2394 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2395 asset->set_start_time (dcp::Time());
2396 for (auto i: subs) {
2397 add_test_subtitle (asset, i.in, i.out, i.v_position, i.v_align, i.text);
2399 asset->set_language (dcp::LanguageTag("de-DE"));
2400 if (key && key_id) {
2401 asset->set_key(*key);
2402 asset->set_key_id(*key_id);
2405 asset->write (dir / "subs.mxf");
2407 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
2408 return write_dcp_with_single_asset (dir, reel_asset);
2413 shared_ptr<dcp::CPL>
2414 dcp_with_text_from_file (path dir, boost::filesystem::path subs_xml)
2416 prepare_directory (dir);
2417 auto asset = make_shared<dcp::SMPTESubtitleAsset>(subs_xml);
2418 asset->set_start_time (dcp::Time());
2419 asset->set_language (dcp::LanguageTag("de-DE"));
2421 auto subs_mxf = dir / "subs.mxf";
2422 asset->write (subs_mxf);
2424 /* The call to write() puts the asset into the DCP correctly but it will have
2425 * XML re-written by our parser. Overwrite the MXF using the given file's verbatim
2428 ASDCP::TimedText::MXFWriter writer;
2429 ASDCP::WriterInfo writer_info;
2430 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
2432 Kumu::hex2bin (asset->id().c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
2433 DCP_ASSERT (c == Kumu::UUID_Length);
2434 ASDCP::TimedText::TimedTextDescriptor descriptor;
2435 descriptor.ContainerDuration = asset->intrinsic_duration();
2436 Kumu::hex2bin (asset->xml_id()->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c);
2437 DCP_ASSERT (c == Kumu::UUID_Length);
2438 ASDCP::Result_t r = writer.OpenWrite (subs_mxf.string().c_str(), writer_info, descriptor, 16384);
2439 BOOST_REQUIRE (!ASDCP_FAILURE(r));
2440 r = writer.WriteTimedTextResource (dcp::file_to_string(subs_xml));
2441 BOOST_REQUIRE (!ASDCP_FAILURE(r));
2444 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
2445 return write_dcp_with_single_asset (dir, reel_asset);
2449 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time)
2451 auto const dir = path("build/test/verify_invalid_subtitle_first_text_time");
2452 /* Just too early */
2453 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
2454 check_verify_result (
2458 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2459 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2460 dcp::VerificationNote(
2461 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2462 ).set_cpl_id(cpl->id()),
2463 dcp::VerificationNote(
2464 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2465 ).set_cpl_id(cpl->id())
2471 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time)
2473 auto const dir = path("build/test/verify_valid_subtitle_first_text_time");
2474 /* Just late enough */
2475 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
2476 check_verify_result(
2480 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2481 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2482 dcp::VerificationNote(
2483 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2484 ).set_cpl_id(cpl->id())
2489 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel)
2491 auto const dir = path("build/test/verify_valid_subtitle_first_text_time_on_second_reel");
2492 prepare_directory (dir);
2494 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
2495 asset1->set_start_time (dcp::Time());
2496 /* Just late enough */
2497 add_test_subtitle (asset1, 4 * 24, 5 * 24);
2498 asset1->set_language (dcp::LanguageTag("de-DE"));
2500 asset1->write (dir / "subs1.mxf");
2501 auto reel_asset1 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset1, dcp::Fraction(24, 1), 5 * 24, 0);
2502 auto reel1 = make_shared<dcp::Reel>();
2503 reel1->add (reel_asset1);
2504 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 5 * 24);
2505 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2506 reel1->add (markers1);
2508 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
2509 asset2->set_start_time (dcp::Time());
2511 /* This would be too early on first reel but should be OK on the second */
2512 add_test_subtitle (asset2, 3, 4 * 24);
2513 asset2->set_language (dcp::LanguageTag("de-DE"));
2514 asset2->write (dir / "subs2.mxf");
2515 auto reel_asset2 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset2, dcp::Fraction(24, 1), 4 * 24, 0);
2516 auto reel2 = make_shared<dcp::Reel>();
2517 reel2->add (reel_asset2);
2518 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 4 * 24);
2519 markers2->set (dcp::Marker::LFOC, dcp::Time(4 * 24 - 1, 24, 24));
2520 reel2->add (markers2);
2522 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2525 auto dcp = make_shared<dcp::DCP>(dir);
2527 dcp->set_annotation_text("hello");
2530 check_verify_result(
2534 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2535 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2536 dcp::VerificationNote(
2537 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2538 ).set_cpl_id(cpl->id())
2543 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing)
2545 auto const dir = path("build/test/verify_invalid_subtitle_spacing");
2546 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2550 { 5 * 24 + 1, 6 * 24 },
2552 check_verify_result (
2556 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2557 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2558 dcp::VerificationNote(
2559 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING
2560 ).set_cpl_id(cpl->id()),
2561 dcp::VerificationNote(
2562 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2563 ).set_cpl_id(cpl->id())
2568 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_spacing)
2570 auto const dir = path("build/test/verify_valid_subtitle_spacing");
2571 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2575 { 5 * 24 + 16, 8 * 24 },
2578 check_verify_result(
2582 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2583 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2584 dcp::VerificationNote(
2585 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2586 ).set_cpl_id(cpl->id())
2591 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_duration)
2593 auto const dir = path("build/test/verify_invalid_subtitle_duration");
2594 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
2595 check_verify_result (
2599 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2600 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2601 dcp::VerificationNote(
2602 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION
2603 ).set_cpl_id(cpl->id()),
2604 dcp::VerificationNote(
2605 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2606 ).set_cpl_id(cpl->id())
2611 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_duration)
2613 auto const dir = path("build/test/verify_valid_subtitle_duration");
2614 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
2616 check_verify_result(
2620 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2621 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2622 dcp::VerificationNote(
2623 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2624 ).set_cpl_id(cpl->id())
2629 BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary)
2631 auto const dir = path("build/test/verify_subtitle_overlapping_reel_boundary");
2632 prepare_directory (dir);
2633 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2634 asset->set_start_time (dcp::Time());
2635 add_test_subtitle (asset, 0, 4 * 24);
2637 asset->set_language (dcp::LanguageTag("de-DE"));
2638 asset->write (dir / "subs.mxf");
2640 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 3 * 24, 0);
2641 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
2642 check_verify_result (
2646 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2647 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2648 dcp::VerificationNote(
2649 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get())
2650 ).set_cpl_id(cpl->id()),
2651 dcp::VerificationNote(
2652 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2653 ).set_cpl_id(cpl->id()),
2654 dcp::VerificationNote(
2655 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY
2656 ).set_cpl_id(cpl->id()),
2657 dcp::VerificationNote(
2658 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2659 ).set_cpl_id(cpl->id())
2665 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count1)
2667 auto const dir = path ("build/test/invalid_subtitle_line_count1");
2668 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2671 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2672 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2673 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2674 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
2676 check_verify_result (
2680 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2681 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2682 dcp::VerificationNote(
2683 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT
2684 ).set_cpl_id(cpl->id()),
2685 dcp::VerificationNote(
2686 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2687 ).set_cpl_id(cpl->id())
2692 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count1)
2694 auto const dir = path ("build/test/verify_valid_subtitle_line_count1");
2695 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2698 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2699 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2700 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2703 check_verify_result(
2707 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2708 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2709 dcp::VerificationNote(
2710 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2711 ).set_cpl_id(cpl->id())
2716 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count2)
2718 auto const dir = path ("build/test/verify_invalid_subtitle_line_count2");
2719 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2722 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2723 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2724 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2725 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2727 check_verify_result (
2731 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2732 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2733 dcp::VerificationNote(
2734 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT
2735 ).set_cpl_id(cpl->id()),
2736 dcp::VerificationNote(
2737 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2738 ).set_cpl_id(cpl->id())
2743 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count2)
2745 auto const dir = path ("build/test/verify_valid_subtitle_line_count2");
2746 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2749 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2750 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2751 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2752 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2755 check_verify_result(
2759 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2760 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2761 dcp::VerificationNote(
2762 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2763 ).set_cpl_id(cpl->id())
2768 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length1)
2770 auto const dir = path ("build/test/verify_invalid_subtitle_line_length1");
2771 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2774 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123" }
2776 check_verify_result (
2780 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2781 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2782 dcp::VerificationNote(
2783 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH
2784 ).set_cpl_id(cpl->id()),
2785 dcp::VerificationNote(
2786 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2787 ).set_cpl_id(cpl->id())
2792 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length2)
2794 auto const dir = path ("build/test/verify_invalid_subtitle_line_length2");
2795 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2798 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
2800 check_verify_result (
2804 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2805 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2806 dcp::VerificationNote(
2807 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH
2808 ).set_cpl_id(cpl->id()),
2809 dcp::VerificationNote(
2810 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2811 ).set_cpl_id(cpl->id())
2816 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count1)
2818 auto const dir = path ("build/test/verify_valid_closed_caption_line_count1");
2819 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2822 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2823 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2824 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2825 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
2827 check_verify_result (
2831 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2832 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2833 dcp::VerificationNote(
2834 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT
2835 ).set_cpl_id(cpl->id()),
2836 dcp::VerificationNote(
2837 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2838 ).set_cpl_id(cpl->id())
2843 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count2)
2845 auto const dir = path ("build/test/verify_valid_closed_caption_line_count2");
2846 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2849 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2850 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2851 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2854 check_verify_result(
2858 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2859 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2860 dcp::VerificationNote(
2861 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2862 ).set_cpl_id(cpl->id())
2867 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_count3)
2869 auto const dir = path ("build/test/verify_invalid_closed_caption_line_count3");
2870 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2873 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2874 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2875 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2876 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2878 check_verify_result (
2882 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2883 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2884 dcp::VerificationNote(
2885 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT
2886 ).set_cpl_id(cpl->id()),
2887 dcp::VerificationNote(
2888 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2889 ).set_cpl_id(cpl->id())
2894 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count4)
2896 auto const dir = path ("build/test/verify_valid_closed_caption_line_count4");
2897 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2900 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2901 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2902 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2903 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2906 check_verify_result(
2910 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2911 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2912 dcp::VerificationNote(
2913 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2914 ).set_cpl_id(cpl->id())
2919 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_length)
2921 auto const dir = path ("build/test/verify_valid_closed_caption_line_length");
2922 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2925 { 96, 300, 0.0, dcp::VAlign::CENTER, "01234567890123456789012345678901" }
2928 check_verify_result (
2932 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2933 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2934 dcp::VerificationNote(
2935 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2936 ).set_cpl_id(cpl->id())
2941 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length)
2943 auto const dir = path ("build/test/verify_invalid_closed_caption_line_length");
2944 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2947 { 96, 300, 0.0, dcp::VAlign::CENTER, "0123456789012345678901234567890123" }
2949 check_verify_result (
2953 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2954 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2955 dcp::VerificationNote(
2956 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH
2957 ).set_cpl_id(cpl->id()),
2958 dcp::VerificationNote(
2959 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2960 ).set_cpl_id(cpl->id())
2965 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1)
2967 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign1");
2968 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2971 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2972 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2973 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2975 check_verify_result (
2979 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2980 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2981 dcp::VerificationNote(
2982 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2983 ).set_cpl_id(cpl->id())
2988 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2)
2990 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign2");
2991 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2994 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2995 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2996 { 96, 300, 0.2, dcp::VAlign::CENTER, "not fine" },
2998 check_verify_result (
3002 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3003 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3004 dcp::VerificationNote(
3005 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN
3006 ).set_cpl_id(cpl->id()),
3007 dcp::VerificationNote(
3008 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3009 ).set_cpl_id(cpl->id())
3014 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1)
3016 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering1");
3017 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3020 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
3021 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
3022 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
3025 check_verify_result(
3029 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3030 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3031 dcp::VerificationNote(
3032 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3033 ).set_cpl_id(cpl->id())
3038 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2)
3040 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering2");
3041 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3044 { 96, 300, 0.2, dcp::VAlign::BOTTOM, "This" },
3045 { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" },
3046 { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" },
3049 check_verify_result(
3053 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3054 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3055 dcp::VerificationNote(
3056 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3057 ).set_cpl_id(cpl->id())
3062 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3)
3064 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering3");
3065 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering3.xml");
3066 check_verify_result (
3070 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3071 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3072 dcp::VerificationNote(
3073 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING
3074 ).set_cpl_id(cpl->id()),
3075 dcp::VerificationNote(
3076 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3077 ).set_cpl_id(cpl->id())
3082 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4)
3084 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4");
3085 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml");
3087 check_verify_result(
3091 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3092 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3093 dcp::VerificationNote(
3094 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3095 ).set_cpl_id(cpl->id())
3101 BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate)
3103 path const dir("build/test/verify_invalid_sound_frame_rate");
3104 prepare_directory (dir);
3106 auto picture = simple_picture (dir, "foo");
3107 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
3108 auto reel = make_shared<dcp::Reel>();
3109 reel->add (reel_picture);
3110 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000, boost::none);
3111 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
3112 reel->add (reel_sound);
3113 reel->add (simple_markers());
3114 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3116 auto dcp = make_shared<dcp::DCP>(dir);
3118 dcp->set_annotation_text("hello");
3121 check_verify_result (
3125 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3126 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "videofoo.mxf"), cpl),
3127 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3128 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "videofoo.mxf"), cpl),
3129 dcp::VerificationNote(
3130 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf")
3131 ).set_cpl_id(cpl->id()),
3132 dcp::VerificationNote(
3133 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3134 ).set_cpl_id(cpl->id())
3139 BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text)
3141 path const dir("build/test/verify_missing_cpl_annotation_text");
3142 auto dcp = make_simple (dir);
3145 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3147 auto const cpl = dcp->cpls()[0];
3149 HashCalculator calc(cpl->file().get());
3152 BOOST_REQUIRE (cpl->file());
3153 Editor e(cpl->file().get());
3154 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
3157 check_verify_result (
3161 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3162 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3163 dcp::VerificationNote(
3164 dcp::VerificationNote::Type::OK,
3165 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3166 string{"1998x1080"},
3168 ).set_cpl_id(cpl->id()),
3169 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3170 dcp::VerificationNote(
3171 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, canonical(cpl->file().get())
3172 ).set_cpl_id(cpl->id()),
3173 dcp::VerificationNote(
3174 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get())
3175 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash())
3180 BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text)
3182 path const dir("build/test/verify_mismatched_cpl_annotation_text");
3183 auto dcp = make_simple (dir);
3186 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3187 auto const cpl = dcp->cpls()[0];
3189 HashCalculator calc(cpl->file().get());
3192 BOOST_REQUIRE (cpl->file());
3193 Editor e(cpl->file().get());
3194 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
3197 check_verify_result (
3201 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3202 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3203 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3204 dcp::VerificationNote(
3205 dcp::VerificationNote::Type::OK,
3206 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3207 string{"1998x1080"},
3209 ).set_cpl_id(cpl->id()),
3210 dcp::VerificationNote(
3211 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, canonical(cpl->file().get())
3212 ).set_cpl_id(cpl->id()),
3213 dcp::VerificationNote(
3214 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get())
3215 ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()).set_cpl_id(cpl->id())
3220 BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration)
3222 path const dir("build/test/verify_mismatched_asset_duration");
3223 prepare_directory (dir);
3224 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
3225 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3227 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
3228 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
3230 auto reel = make_shared<dcp::Reel>(
3231 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
3232 make_shared<dcp::ReelSoundAsset>(ms, 0)
3235 reel->add (simple_markers());
3239 dcp->set_annotation_text("A Test DCP");
3242 check_verify_result (
3246 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3247 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3248 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3249 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3250 dcp::VerificationNote(
3251 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION
3252 ).set_cpl_id(cpl->id()),
3253 dcp::VerificationNote(
3254 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl->file().get())
3255 ).set_cpl_id(cpl->id())
3262 shared_ptr<dcp::CPL>
3263 verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool add_to_reel2)
3265 prepare_directory (dir);
3266 auto dcp = make_shared<dcp::DCP>(dir);
3267 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3269 auto constexpr reel_length = 192;
3271 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
3272 subs->set_language (dcp::LanguageTag("de-DE"));
3273 subs->set_start_time (dcp::Time());
3274 subs->add (simple_subtitle());
3276 subs->write (dir / "subs.mxf");
3277 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
3279 auto reel1 = make_shared<dcp::Reel>(
3280 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "1", reel_length), 0),
3281 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "1", dcp::MXFMetadata(), "en-US", reel_length), 0)
3285 reel1->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3288 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3289 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
3290 reel1->add (markers1);
3294 auto reel2 = make_shared<dcp::Reel>(
3295 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "2", reel_length), 0),
3296 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "2", dcp::MXFMetadata(), "en-US", reel_length), 0)
3300 reel2->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3303 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3304 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
3305 reel2->add (markers2);
3310 dcp->set_annotation_text("A Test DCP");
3317 BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels)
3320 path dir ("build/test/missing_main_subtitle_from_some_reels");
3321 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, false);
3322 check_verify_result (
3326 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3327 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3328 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3329 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3330 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3331 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3332 dcp::VerificationNote(
3333 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS
3334 ).set_cpl_id(cpl->id()),
3335 dcp::VerificationNote(
3336 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3337 ).set_cpl_id(cpl->id())
3343 path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
3344 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, true);
3345 check_verify_result(
3349 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3350 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3351 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3352 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3353 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3354 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3355 dcp::VerificationNote(
3356 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3357 ).set_cpl_id(cpl->id())
3362 path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
3363 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, false, false);
3364 check_verify_result(
3368 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3369 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3370 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3371 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3372 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3373 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3374 dcp::VerificationNote(
3375 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3376 ).set_cpl_id(cpl->id())
3383 shared_ptr<dcp::CPL>
3384 verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, int caps_in_reel2)
3386 prepare_directory (dir);
3387 auto dcp = make_shared<dcp::DCP>(dir);
3388 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3390 auto constexpr reel_length = 192;
3392 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
3393 subs->set_language (dcp::LanguageTag("de-DE"));
3394 subs->set_start_time (dcp::Time());
3395 subs->add (simple_subtitle());
3397 subs->write (dir / "subs.mxf");
3399 auto reel1 = make_shared<dcp::Reel>(
3400 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "1", reel_length), 0),
3401 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "1", dcp::MXFMetadata(), "en-US", reel_length), 0)
3404 for (int i = 0; i < caps_in_reel1; ++i) {
3405 reel1->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3408 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3409 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
3410 reel1->add (markers1);
3414 auto reel2 = make_shared<dcp::Reel>(
3415 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "2", reel_length), 0),
3416 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "2", dcp::MXFMetadata(), "en-US", reel_length), 0)
3419 for (int i = 0; i < caps_in_reel2; ++i) {
3420 reel2->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3423 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3424 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
3425 reel2->add (markers2);
3430 dcp->set_annotation_text("A Test DCP");
3437 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_asset_counts)
3440 path dir ("build/test/mismatched_closed_caption_asset_counts");
3441 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
3442 check_verify_result (
3446 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3447 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3448 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3449 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3450 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3451 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3452 dcp::VerificationNote(
3453 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS
3454 ).set_cpl_id(cpl->id()),
3455 dcp::VerificationNote(
3456 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3457 ).set_cpl_id(cpl->id())
3462 path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
3463 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
3464 check_verify_result(
3468 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3469 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3470 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3471 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3472 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3473 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3474 dcp::VerificationNote(
3475 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3476 ).set_cpl_id(cpl->id())
3481 path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
3482 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
3483 check_verify_result(
3487 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3488 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3489 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3490 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3491 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3492 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3493 dcp::VerificationNote(
3494 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3495 ).set_cpl_id(cpl->id())
3503 verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
3505 prepare_directory (dir);
3506 auto dcp = make_shared<dcp::DCP>(dir);
3507 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3509 auto constexpr reel_length = 192;
3511 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
3512 subs->set_language (dcp::LanguageTag("de-DE"));
3513 subs->set_start_time (dcp::Time());
3514 subs->add (simple_subtitle());
3516 subs->write (dir / "subs.mxf");
3517 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), reel_length, 0);
3520 auto reel = make_shared<dcp::Reel>(
3521 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
3522 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
3525 reel->add (reel_text);
3527 reel->add (simple_markers(reel_length));
3532 dcp->set_annotation_text("A Test DCP");
3535 check_verify_result (
3539 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3540 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3541 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3542 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3543 dcp::VerificationNote(
3544 dcp::VerificationNote::Type::BV21_ERROR, code, subs->id()
3545 ).set_cpl_id(cpl->id()),
3546 dcp::VerificationNote(
3547 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3548 ).set_cpl_id(cpl->id())
3553 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
3555 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
3556 "build/test/verify_subtitle_entry_point_must_be_present",
3557 dcp::VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT,
3558 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
3559 asset->unset_entry_point ();
3563 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
3564 "build/test/verify_subtitle_entry_point_must_be_zero",
3565 dcp::VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT,
3566 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
3567 asset->set_entry_point (4);
3571 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
3572 "build/test/verify_closed_caption_entry_point_must_be_present",
3573 dcp::VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT,
3574 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
3575 asset->unset_entry_point ();
3579 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
3580 "build/test/verify_closed_caption_entry_point_must_be_zero",
3581 dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT,
3582 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
3583 asset->set_entry_point (9);
3589 BOOST_AUTO_TEST_CASE (verify_missing_hash)
3593 path const dir("build/test/verify_missing_hash");
3594 auto dcp = make_simple (dir);
3597 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3598 auto const cpl = dcp->cpls()[0];
3599 BOOST_REQUIRE_EQUAL (cpl->reels().size(), 1U);
3600 BOOST_REQUIRE (cpl->reels()[0]->main_picture());
3601 auto asset_id = cpl->reels()[0]->main_picture()->id();
3603 HashCalculator calc(cpl->file().get());
3606 BOOST_REQUIRE (cpl->file());
3607 Editor e(cpl->file().get());
3608 e.delete_first_line_containing("<Hash>");
3611 check_verify_result (
3615 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3616 dcp::VerificationNote(
3617 dcp::VerificationNote::Type::OK,
3618 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3619 string{"1998x1080"},
3621 ).set_cpl_id(cpl->id()),
3622 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3623 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3624 dcp::VerificationNote(
3625 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3626 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3627 dcp::VerificationNote(
3628 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id
3629 ).set_cpl_id(cpl->id())
3636 verify_markers_test (
3638 vector<pair<dcp::Marker, dcp::Time>> markers,
3639 vector<dcp::VerificationNote> test_notes
3642 auto dcp = make_simple (dir);
3643 auto cpl = dcp->cpls()[0];
3644 cpl->set_content_kind(dcp::ContentKind::FEATURE);
3645 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24);
3646 for (auto const& i: markers) {
3647 markers_asset->set (i.first, i.second);
3649 cpl->reels()[0]->add(markers_asset);
3652 for (auto& note: test_notes) {
3653 note.set_cpl_id(cpl->id());
3656 test_notes.push_back(ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl));
3657 test_notes.push_back(ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl));
3658 test_notes.push_back(ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl));
3659 test_notes.push_back(
3660 dcp::VerificationNote(
3661 dcp::VerificationNote::Type::OK,
3662 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3663 string{"1998x1080"},
3665 ).set_cpl_id(cpl->id())
3667 test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl));
3668 check_verify_result({dir}, {}, test_notes);
3672 BOOST_AUTO_TEST_CASE (verify_markers)
3674 verify_markers_test (
3675 "build/test/verify_markers_all_correct",
3677 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3678 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3679 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3680 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3685 verify_markers_test (
3686 "build/test/verify_markers_missing_ffec",
3688 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3689 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3690 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3693 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }
3696 verify_markers_test (
3697 "build/test/verify_markers_missing_ffmc",
3699 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3700 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3701 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3704 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }
3707 verify_markers_test (
3708 "build/test/verify_markers_missing_ffoc",
3710 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3711 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3712 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3715 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC}
3718 verify_markers_test (
3719 "build/test/verify_markers_missing_lfoc",
3721 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3722 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3723 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
3726 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }
3729 verify_markers_test (
3730 "build/test/verify_markers_incorrect_ffoc",
3732 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3733 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3734 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
3735 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3738 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_FFOC, string("3") }
3741 verify_markers_test (
3742 "build/test/verify_markers_incorrect_lfoc",
3744 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3745 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3746 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3747 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
3750 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_LFOC, string("18") }
3755 BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number)
3757 path dir = "build/test/verify_missing_cpl_metadata_version_number";
3758 prepare_directory (dir);
3759 auto dcp = make_simple (dir);
3760 auto cpl = dcp->cpls()[0];
3761 cpl->unset_version_number();
3764 check_verify_result(
3768 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3769 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3770 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3771 dcp::VerificationNote(
3772 dcp::VerificationNote::Type::OK,
3773 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3774 string{"1998x1080"},
3776 ).set_cpl_id(cpl->id()),
3777 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3778 dcp::VerificationNote(
3779 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->file().get()
3780 ).set_cpl_id(cpl->id())
3785 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1)
3787 path dir = "build/test/verify_missing_extension_metadata1";
3788 auto dcp = make_simple (dir);
3791 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3792 auto cpl = dcp->cpls()[0];
3794 HashCalculator calc(cpl->file().get());
3797 Editor e (cpl->file().get());
3798 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
3801 check_verify_result (
3805 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3806 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3807 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3808 dcp::VerificationNote(
3809 dcp::VerificationNote::Type::OK,
3810 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3811 string{"1998x1080"},
3813 ).set_cpl_id(cpl->id()),
3814 dcp::VerificationNote(
3815 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3816 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3817 dcp::VerificationNote(
3818 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get()
3819 ).set_cpl_id(cpl->id())
3824 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2)
3826 path dir = "build/test/verify_missing_extension_metadata2";
3827 auto dcp = make_simple (dir);
3830 auto cpl = dcp->cpls()[0];
3832 HashCalculator calc(cpl->file().get());
3835 Editor e (cpl->file().get());
3836 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
3839 check_verify_result (
3843 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3844 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3845 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3846 dcp::VerificationNote(
3847 dcp::VerificationNote::Type::OK,
3848 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3849 string{"1998x1080"},
3851 ).set_cpl_id(cpl->id()),
3852 dcp::VerificationNote(
3853 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3854 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3855 dcp::VerificationNote(
3856 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get()
3857 ).set_cpl_id(cpl->id())
3862 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3)
3864 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata3";
3865 auto dcp = make_simple (dir);
3868 auto const cpl = dcp->cpls()[0];
3870 HashCalculator calc(cpl->file().get());
3873 Editor e (cpl->file().get());
3874 e.replace ("<meta:Name>A", "<meta:NameX>A");
3875 e.replace ("n</meta:Name>", "n</meta:NameX>");
3878 check_verify_result (
3882 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3883 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3884 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3885 dcp::VerificationNote(
3886 dcp::VerificationNote::Type::OK,
3887 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3888 string{"1998x1080"},
3890 ).set_cpl_id(cpl->id()),
3891 dcp::VerificationNote(
3892 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70
3893 ).set_cpl_id(cpl->id()),
3894 dcp::VerificationNote(
3895 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:NameX' is not allowed for content model '(Name,PropertyList?,)'"), cpl->file().get(), 77).set_cpl_id(cpl->id()),
3896 dcp::VerificationNote(
3897 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3898 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3903 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1)
3905 path dir = "build/test/verify_invalid_extension_metadata1";
3906 auto dcp = make_simple (dir);
3909 auto cpl = dcp->cpls()[0];
3911 HashCalculator calc(cpl->file().get());
3914 Editor e (cpl->file().get());
3915 e.replace ("Application", "Fred");
3918 check_verify_result (
3922 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3923 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3924 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3925 dcp::VerificationNote(
3926 dcp::VerificationNote::Type::OK,
3927 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3928 string{"1998x1080"},
3930 ).set_cpl_id(cpl->id()),
3931 dcp::VerificationNote(
3932 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3933 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3934 dcp::VerificationNote(
3935 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get()
3936 ).set_cpl_id(cpl->id())
3941 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2)
3943 path dir = "build/test/verify_invalid_extension_metadata2";
3944 auto dcp = make_simple (dir);
3947 auto cpl = dcp->cpls()[0];
3949 HashCalculator calc(cpl->file().get());
3952 Editor e (cpl->file().get());
3953 e.replace ("DCP Constraints Profile", "Fred");
3956 check_verify_result (
3960 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3961 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3962 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3963 dcp::VerificationNote(
3964 dcp::VerificationNote::Type::OK,
3965 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3966 string{"1998x1080"},
3968 ).set_cpl_id(cpl->id()),
3969 dcp::VerificationNote(
3970 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3971 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3972 dcp::VerificationNote(
3973 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get()
3974 ).set_cpl_id(cpl->id())
3979 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6)
3981 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata6";
3982 auto dcp = make_simple (dir);
3985 auto const cpl = dcp->cpls()[0];
3987 HashCalculator calc(cpl->file().get());
3990 Editor e (cpl->file().get());
3991 e.replace ("<meta:Value>", "<meta:ValueX>");
3992 e.replace ("</meta:Value>", "</meta:ValueX>");
3995 check_verify_result (
3999 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4000 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4001 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4002 dcp::VerificationNote(
4003 dcp::VerificationNote::Type::OK,
4004 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4005 string{"1998x1080"},
4007 ).set_cpl_id(cpl->id()),
4008 dcp::VerificationNote(
4009 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74
4010 ).set_cpl_id(cpl->id()),
4011 dcp::VerificationNote(
4012 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:ValueX' is not allowed for content model '(Name,Value)'"), cpl->file().get(), 75
4013 ).set_cpl_id(cpl->id()),
4014 dcp::VerificationNote(
4015 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4016 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash())
4021 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7)
4023 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata7";
4024 auto dcp = make_simple (dir);
4027 auto const cpl = dcp->cpls()[0];
4029 HashCalculator calc(cpl->file().get());
4032 Editor e (cpl->file().get());
4033 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
4036 check_verify_result (
4040 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4041 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4042 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4043 dcp::VerificationNote(
4044 dcp::VerificationNote::Type::OK,
4045 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4046 string{"1998x1080"},
4048 ).set_cpl_id(cpl->id()),
4049 dcp::VerificationNote(
4050 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4051 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4052 dcp::VerificationNote(
4053 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'"), cpl->file().get()
4054 ).set_cpl_id(cpl->id())
4059 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8)
4061 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata8";
4062 auto dcp = make_simple (dir);
4065 auto const cpl = dcp->cpls()[0];
4067 HashCalculator calc(cpl->file().get());
4070 Editor e (cpl->file().get());
4071 e.replace ("<meta:Property>", "<meta:PropertyX>");
4072 e.replace ("</meta:Property>", "</meta:PropertyX>");
4075 check_verify_result (
4079 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4080 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4081 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4082 dcp::VerificationNote(
4083 dcp::VerificationNote::Type::OK,
4084 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4085 string{"1998x1080"},
4087 ).set_cpl_id(cpl->id()),
4088 dcp::VerificationNote(
4089 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72
4090 ).set_cpl_id(cpl->id()),
4091 dcp::VerificationNote(
4092 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyX' is not allowed for content model '(Property+)'"), cpl->file().get(), 76).set_cpl_id(cpl->id()),
4093 dcp::VerificationNote(
4094 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4095 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4100 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9)
4102 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata9";
4103 auto dcp = make_simple (dir);
4106 auto const cpl = dcp->cpls()[0];
4108 HashCalculator calc(cpl->file().get());
4111 Editor e (cpl->file().get());
4112 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
4113 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
4116 check_verify_result (
4120 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4121 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4122 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4123 dcp::VerificationNote(
4124 dcp::VerificationNote::Type::OK,
4125 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4126 string{"1998x1080"},
4128 ).set_cpl_id(cpl->id()),
4129 dcp::VerificationNote(
4130 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71
4131 ).set_cpl_id(cpl->id()),
4132 dcp::VerificationNote(
4133 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyListX' is not allowed for content model '(Name,PropertyList?,)'"), cpl->file().get(), 77
4134 ).set_cpl_id(cpl->id()),
4135 dcp::VerificationNote(
4136 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4137 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4143 BOOST_AUTO_TEST_CASE (verify_unsigned_cpl_with_encrypted_content)
4145 path const dir = "build/test/verify_unsigned_cpl_with_encrypted_content";
4146 prepare_directory (dir);
4147 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
4148 copy_file (i.path(), dir / i.path().filename());
4151 path const pkl = dir / ( "pkl_" + encryption_test_pkl_id() + ".xml");
4152 path const cpl_path = dir / ( "cpl_" + encryption_test_cpl_id() + ".xml");
4154 HashCalculator calc(cpl_path);
4158 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
4161 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
4163 check_verify_result (
4167 ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl),
4168 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4169 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4170 dcp::VerificationNote(
4171 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
4172 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4173 dcp::VerificationNote(
4174 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl)
4175 ).set_cpl_id(cpl->id()),
4176 dcp::VerificationNote(
4177 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE
4178 ).set_cpl_id(cpl->id()),
4179 dcp::VerificationNote(
4180 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE
4181 ).set_cpl_id(cpl->id()),
4182 dcp::VerificationNote(
4183 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC
4184 ).set_cpl_id(cpl->id()),
4185 dcp::VerificationNote(
4186 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC
4187 ).set_cpl_id(cpl->id()),
4188 dcp::VerificationNote(
4189 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_path)
4190 ).set_cpl_id(cpl->id()),
4191 dcp::VerificationNote(
4192 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, canonical(cpl_path)
4193 ).set_cpl_id(cpl->id())
4198 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_encrypted_content)
4200 path dir = "build/test/unsigned_pkl_with_encrypted_content";
4201 prepare_directory (dir);
4202 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
4203 copy_file (i.path(), dir / i.path().filename());
4206 path const cpl_path = dir / ("cpl_" + encryption_test_cpl_id() + ".xml");
4207 path const pkl = dir / ("pkl_" + encryption_test_pkl_id() + ".xml");
4210 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
4213 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
4215 check_verify_result (
4219 ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl),
4220 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4221 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4222 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4223 dcp::VerificationNote(
4224 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl)
4225 ).set_cpl_id(cpl->id()),
4226 dcp::VerificationNote(
4227 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE
4228 ).set_cpl_id(cpl->id()),
4229 dcp::VerificationNote(
4230 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE
4231 ).set_cpl_id(cpl->id()),
4232 dcp::VerificationNote(
4233 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC
4234 ).set_cpl_id(cpl->id()),
4235 dcp::VerificationNote(
4236 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC
4237 ).set_cpl_id(cpl->id()),
4238 dcp::VerificationNote(
4239 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_path)
4240 ).set_cpl_id(cpl->id()),
4241 dcp::VerificationNote(
4242 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id(), canonical(pkl)
4248 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_unencrypted_content)
4250 path dir = "build/test/verify_unsigned_pkl_with_unencrypted_content";
4251 prepare_directory (dir);
4252 for (auto i: directory_iterator("test/ref/DCP/dcp_test1")) {
4253 copy_file (i.path(), dir / i.path().filename());
4257 Editor e (dir / dcp_test1_pkl());
4258 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
4261 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
4263 check_verify_result(
4267 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4268 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4269 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4270 dcp::VerificationNote(
4271 dcp::VerificationNote::Type::OK,
4272 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4273 string{"1998x1080"},
4274 canonical(cpl->file().get())
4275 ).set_cpl_id(cpl->id()),
4276 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4281 BOOST_AUTO_TEST_CASE (verify_partially_encrypted)
4283 path dir ("build/test/verify_must_not_be_partially_encrypted");
4284 prepare_directory (dir);
4288 auto signer = make_shared<dcp::CertificateChain>();
4289 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
4290 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
4291 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
4292 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
4294 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
4298 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
4301 auto writer = mp->start_write(dir / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
4302 dcp::ArrayData j2c ("test/data/flat_red.j2c");
4303 for (int i = 0; i < 24; ++i) {
4304 writer->write (j2c.data(), j2c.size());
4306 writer->finalize ();
4308 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
4310 auto reel = make_shared<dcp::Reel>(
4311 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
4312 make_shared<dcp::ReelSoundAsset>(ms, 0)
4315 reel->add (simple_markers());
4319 cpl->set_content_version (
4320 {"urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00", "81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00"}
4322 cpl->set_annotation_text ("A Test DCP");
4323 cpl->set_issuer ("OpenDCP 0.0.25");
4324 cpl->set_creator ("OpenDCP 0.0.25");
4325 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
4326 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
4327 cpl->set_main_sound_sample_rate (48000);
4328 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
4329 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
4330 cpl->set_version_number (1);
4334 d.set_issuer("OpenDCP 0.0.25");
4335 d.set_creator("OpenDCP 0.0.25");
4336 d.set_issue_date("2012-07-17T04:45:18+00:00");
4337 d.set_annotation_text("A Test DCP");
4338 d.write_xml(signer);
4340 check_verify_result (
4344 dcp::VerificationNote(
4345 dcp::VerificationNote::Type::OK,
4346 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4347 string{"1440x1080"},
4349 ).set_cpl_id(cpl->id()),
4350 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4351 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4352 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4353 dcp::VerificationNote(
4354 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED
4355 ).set_cpl_id(cpl->id())
4360 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k)
4362 vector<dcp::VerificationNote> notes;
4363 dcp::MonoPictureAsset picture (find_file(private_test / "data" / "JourneyToJah_TLR-1_F_EN-DE-FR_CH_51_2K_LOK_20140225_DGL_SMPTE_OV", "j2c.mxf"));
4364 auto reader = picture.start_read ();
4365 auto frame = reader->get_frame (0);
4366 verify_j2k(frame, 0, 0, 24, notes);
4367 BOOST_CHECK(notes.empty());
4371 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k)
4373 vector<dcp::VerificationNote> notes;
4374 dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR"));
4375 auto reader = picture.start_read ();
4376 auto frame = reader->get_frame (0);
4377 verify_j2k(frame, 0, 0, 24, notes);
4378 BOOST_CHECK(notes.empty());
4382 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp)
4384 boost::filesystem::path dir = "build/test/verify_jpeg2000_codestream_libdcp";
4385 prepare_directory (dir);
4386 auto dcp = make_simple (dir);
4388 vector<dcp::VerificationNote> notes;
4389 dcp::MonoPictureAsset picture (find_file(dir, "video"));
4390 auto reader = picture.start_read ();
4391 auto frame = reader->get_frame (0);
4392 verify_j2k(frame, 0, 0, 24, notes);
4393 BOOST_CHECK(notes.empty());
4397 /** Check that ResourceID and the XML ID being different is spotted */
4398 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id)
4400 boost::filesystem::path const dir = "build/test/verify_mismatched_subtitle_resource_id";
4401 prepare_directory (dir);
4403 ASDCP::WriterInfo writer_info;
4404 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
4407 auto mxf_id = dcp::make_uuid ();
4408 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
4409 BOOST_REQUIRE (c == Kumu::UUID_Length);
4411 auto resource_id = dcp::make_uuid ();
4412 ASDCP::TimedText::TimedTextDescriptor descriptor;
4413 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
4414 DCP_ASSERT (c == Kumu::UUID_Length);
4416 auto xml_id = dcp::make_uuid ();
4417 ASDCP::TimedText::MXFWriter writer;
4418 auto subs_mxf = dir / "subs.mxf";
4419 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
4420 BOOST_REQUIRE (ASDCP_SUCCESS(r));
4421 writer.WriteTimedTextResource (dcp::String::compose(
4422 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
4423 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
4424 "<Id>urn:uuid:%1</Id>"
4425 "<ContentTitleText>Content</ContentTitleText>"
4426 "<AnnotationText>Annotation</AnnotationText>"
4427 "<IssueDate>2018-10-02T12:25:14</IssueDate>"
4428 "<ReelNumber>1</ReelNumber>"
4429 "<Language>en-US</Language>"
4430 "<EditRate>25 1</EditRate>"
4431 "<TimeCodeRate>25</TimeCodeRate>"
4432 "<StartTime>00:00:00:00</StartTime>"
4433 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
4435 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
4436 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
4437 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
4446 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
4447 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
4449 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
4451 check_verify_result (
4455 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4456 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4457 dcp::VerificationNote(
4458 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf)
4459 ).set_cpl_id(cpl->id()),
4460 dcp::VerificationNote(
4461 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID
4462 ).set_cpl_id(cpl->id()),
4463 dcp::VerificationNote(
4464 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
4465 ).set_cpl_id(cpl->id()),
4466 dcp::VerificationNote(
4467 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
4468 ).set_cpl_id(cpl->id())
4473 /** Check that ResourceID and the MXF ID being the same is spotted */
4474 BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id)
4476 boost::filesystem::path const dir = "build/test/verify_incorrect_timed_text_id";
4477 prepare_directory (dir);
4479 ASDCP::WriterInfo writer_info;
4480 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
4483 auto mxf_id = dcp::make_uuid ();
4484 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
4485 BOOST_REQUIRE (c == Kumu::UUID_Length);
4487 auto resource_id = mxf_id;
4488 ASDCP::TimedText::TimedTextDescriptor descriptor;
4489 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
4490 DCP_ASSERT (c == Kumu::UUID_Length);
4492 auto xml_id = resource_id;
4493 ASDCP::TimedText::MXFWriter writer;
4494 auto subs_mxf = dir / "subs.mxf";
4495 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
4496 BOOST_REQUIRE (ASDCP_SUCCESS(r));
4497 writer.WriteTimedTextResource (dcp::String::compose(
4498 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
4499 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
4500 "<Id>urn:uuid:%1</Id>"
4501 "<ContentTitleText>Content</ContentTitleText>"
4502 "<AnnotationText>Annotation</AnnotationText>"
4503 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
4504 "<ReelNumber>1</ReelNumber>"
4505 "<Language>en-US</Language>"
4506 "<EditRate>25 1</EditRate>"
4507 "<TimeCodeRate>25</TimeCodeRate>"
4508 "<StartTime>00:00:00:00</StartTime>"
4509 "<LoadFont ID=\"font\">urn:uuid:0ce6e0ba-58b9-4344-8929-4d9c959c2d55</LoadFont>"
4511 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
4512 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
4513 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
4522 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
4523 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
4525 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
4527 check_verify_result (
4531 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4532 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4533 dcp::VerificationNote(
4534 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf)
4535 ).set_cpl_id(cpl->id()),
4536 dcp::VerificationNote(
4537 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID
4538 ).set_cpl_id(cpl->id()),
4539 dcp::VerificationNote(
4540 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
4541 ).set_cpl_id(cpl->id()),
4542 dcp::VerificationNote(
4543 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
4544 ).set_cpl_id(cpl->id()),
4545 dcp::VerificationNote(
4546 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"}
4547 ).set_cpl_id(cpl->id())
4552 /** Check a DCP with a 3D asset marked as 2D */
4553 BOOST_AUTO_TEST_CASE (verify_threed_marked_as_twod)
4555 auto const path = private_test / "data" / "xm";
4557 auto cpl = std::make_shared<dcp::CPL>(find_prefix(path, "CPL_"));
4560 check_verify_result (
4564 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4565 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "0d6f57e6-adac-4e1d-bfbe-d162bf13e2cd_j2c.mxf"), cpl),
4566 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "0d6f57e6-adac-4e1d-bfbe-d162bf13e2cd_j2c.mxf"), cpl),
4567 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4568 dcp::VerificationNote(
4569 dcp::VerificationNote::Type::WARNING,
4570 dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(path, "j2c"))
4572 dcp::VerificationNote(
4573 dcp::VerificationNote::Type::BV21_ERROR,
4574 dcp::VerificationNote::Code::INVALID_STANDARD
4581 BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers)
4583 path dir = "build/test/verify_unexpected_things_in_main_markers";
4584 prepare_directory (dir);
4585 auto dcp = make_simple (dir, 1, 24);
4588 HashCalculator calc(find_cpl(dir));
4591 Editor e (find_cpl(dir));
4593 " <IntrinsicDuration>24</IntrinsicDuration>",
4594 "<EntryPoint>0</EntryPoint><Duration>24</Duration>"
4598 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
4600 check_verify_result (
4604 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4605 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4606 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4607 dcp::VerificationNote(
4608 dcp::VerificationNote::Type::OK,
4609 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4610 string{"1998x1080"},
4611 canonical(cpl->file().get())
4612 ).set_cpl_id(cpl->id()),
4613 dcp::VerificationNote(
4614 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4615 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4616 dcp::VerificationNote(
4617 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT
4618 ).set_cpl_id(cpl->id()),
4619 dcp::VerificationNote(
4620 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION
4621 ).set_cpl_id(cpl->id())
4626 BOOST_AUTO_TEST_CASE(verify_invalid_content_kind)
4628 path dir = "build/test/verify_invalid_content_kind";
4629 prepare_directory (dir);
4630 auto dcp = make_simple (dir, 1, 24);
4633 HashCalculator calc(find_cpl(dir));
4636 Editor e(find_cpl(dir));
4637 e.replace("trailer", "trip");
4640 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4642 check_verify_result (
4646 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4647 dcp::VerificationNote(
4648 dcp::VerificationNote::Type::OK,
4649 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4650 string{"1998x1080"},
4651 canonical(cpl->file().get())
4652 ).set_cpl_id(cpl->id()),
4653 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4654 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4655 dcp::VerificationNote(
4656 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4657 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4658 dcp::VerificationNote(
4659 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip")
4660 ).set_cpl_id(cpl->id()),
4666 BOOST_AUTO_TEST_CASE(verify_valid_content_kind)
4668 path dir = "build/test/verify_valid_content_kind";
4669 prepare_directory (dir);
4670 auto dcp = make_simple (dir, 1, 24);
4673 HashCalculator calc(find_cpl(dir));
4676 Editor e(find_cpl(dir));
4677 e.replace("<ContentKind>trailer</ContentKind>", "<ContentKind scope=\"http://bobs.contents/\">trip</ContentKind>");
4680 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4682 check_verify_result (
4686 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4687 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4688 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4689 dcp::VerificationNote(
4690 dcp::VerificationNote::Type::OK,
4691 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4692 string{"1998x1080"},
4693 canonical(cpl->file().get())
4694 ).set_cpl_id(cpl->id()),
4695 dcp::VerificationNote(
4696 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4697 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4702 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1)
4704 path dir = "build/test/verify_invalid_main_picture_active_area_1";
4705 prepare_directory(dir);
4706 auto dcp = make_simple(dir, 1, 24);
4709 auto constexpr area = "<meta:MainPictureActiveArea>";
4711 HashCalculator calc(find_cpl(dir));
4714 Editor e(find_cpl(dir));
4715 e.delete_lines_after(area, 2);
4716 e.insert(area, "<meta:Height>4080</meta:Height>");
4717 e.insert(area, "<meta:Width>1997</meta:Width>");
4720 dcp::PKL pkl(find_pkl(dir));
4721 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4723 check_verify_result(
4727 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4728 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4729 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4730 dcp::VerificationNote(
4731 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4732 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4733 dcp::VerificationNote(
4734 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir))
4735 ).set_cpl_id(cpl->id()),
4736 dcp::VerificationNote(
4737 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 4080 is bigger than the asset height 1080", canonical(find_cpl(dir))
4738 ).set_cpl_id(cpl->id()),
4743 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2)
4745 path dir = "build/test/verify_invalid_main_picture_active_area_2";
4746 prepare_directory(dir);
4747 auto dcp = make_simple(dir, 1, 24);
4750 auto constexpr area = "<meta:MainPictureActiveArea>";
4752 HashCalculator calc(find_cpl(dir));
4755 Editor e(find_cpl(dir));
4756 e.delete_lines_after(area, 2);
4757 e.insert(area, "<meta:Height>5125</meta:Height>");
4758 e.insert(area, "<meta:Width>9900</meta:Width>");
4761 dcp::PKL pkl(find_pkl(dir));
4762 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4764 check_verify_result(
4768 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4769 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4770 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4771 dcp::VerificationNote(
4772 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4773 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4774 dcp::VerificationNote(
4775 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir))
4776 ).set_cpl_id(cpl->id()),
4777 dcp::VerificationNote(
4778 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 9900 is bigger than the asset width 1998", canonical(find_cpl(dir))
4779 ).set_cpl_id(cpl->id()),
4780 dcp::VerificationNote(
4781 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is bigger than the asset height 1080", canonical(find_cpl(dir))
4782 ).set_cpl_id(cpl->id())
4787 BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids)
4791 path dir = "build/test/verify_duplicate_pkl_asset_ids";
4792 prepare_directory(dir);
4793 auto dcp = make_simple(dir, 1, 24);
4797 Editor e(find_pkl(dir));
4798 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab");
4801 dcp::PKL pkl(find_pkl(dir));
4802 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4804 check_verify_result(
4808 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4809 dcp::VerificationNote(
4810 dcp::VerificationNote::Type::OK,
4811 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4812 string{"1998x1080"},
4813 canonical(cpl->file().get())
4814 ).set_cpl_id(cpl->id()),
4815 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4816 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) },
4821 BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids)
4825 path dir = "build/test/verify_duplicate_assetmap_asset_ids";
4826 prepare_directory(dir);
4827 auto dcp = make_simple(dir, 1, 24);
4831 Editor e(find_asset_map(dir));
4832 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa");
4835 dcp::PKL pkl(find_pkl(dir));
4836 dcp::AssetMap asset_map(find_asset_map(dir));
4837 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
4839 check_verify_result(
4843 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4844 dcp::VerificationNote(
4845 dcp::VerificationNote::Type::OK,
4846 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4847 string{"1998x1080"},
4848 canonical(cpl->file().get())
4849 ).set_cpl_id(cpl->id()),
4850 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4851 dcp::VerificationNote(
4852 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir))
4854 dcp::VerificationNote(
4855 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54")
4861 BOOST_AUTO_TEST_CASE(verify_mismatched_sound_channel_counts)
4863 boost::filesystem::path const path = "build/test/verify_mismatched_sound_channel_counts";
4865 dcp::MXFMetadata mxf_meta;
4866 mxf_meta.company_name = "OpenDCP";
4867 mxf_meta.product_name = "OpenDCP";
4868 mxf_meta.product_version = "0.0.25";
4870 auto constexpr sample_rate = 48000;
4871 auto constexpr frames = 240;
4873 boost::filesystem::remove_all(path);
4874 boost::filesystem::create_directories(path);
4875 auto dcp = make_shared<dcp::DCP>(path);
4876 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
4877 cpl->set_annotation_text("hello");
4878 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R"));
4879 cpl->set_main_sound_sample_rate(sample_rate);
4880 cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
4881 cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
4882 cpl->set_version_number(1);
4886 /* Reel with 2 channels of audio */
4888 auto mp = simple_picture(path, "1", frames, {});
4889 auto ms = simple_sound(path, "1", mxf_meta, "en-US", frames, sample_rate, {}, 2);
4891 auto reel = make_shared<dcp::Reel>(
4892 std::make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
4893 std::make_shared<dcp::ReelSoundAsset>(ms, 0)
4896 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), frames);
4897 markers->set(dcp::Marker::FFOC, dcp::Time(0, 0, 0, 1, 24));
4904 /* Reel with 6 channels of audio */
4906 auto mp = simple_picture(path, "2", frames, {});
4907 auto ms = simple_sound(path, "2", mxf_meta, "en-US", frames, sample_rate, {}, 6);
4909 auto reel = make_shared<dcp::Reel>(
4910 std::make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
4911 std::make_shared<dcp::ReelSoundAsset>(ms, 0)
4914 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), frames);
4915 markers->set(dcp::Marker::LFOC, dcp::Time(0, 0, 0, frames - 1, 24));
4922 dcp->set_annotation_text("hello");
4925 check_verify_result(
4929 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4930 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4931 dcp::VerificationNote(
4932 dcp::VerificationNote::Type::OK,
4933 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4934 string{"1998x1080"},
4936 ).set_cpl_id(cpl->id()),
4937 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
4938 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
4939 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video2.mxf"), cpl),
4940 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video2.mxf"), cpl),
4941 dcp::VerificationNote(
4942 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, canonical(find_file(path, "audio2"))
4943 ).set_cpl_id(cpl->id())
4948 BOOST_AUTO_TEST_CASE(verify_invalid_main_sound_configuration)
4950 boost::filesystem::path const path = "build/test/verify_invalid_main_sound_configuration";
4952 dcp::MXFMetadata mxf_meta;
4953 mxf_meta.company_name = "OpenDCP";
4954 mxf_meta.product_name = "OpenDCP";
4955 mxf_meta.product_version = "0.0.25";
4957 auto constexpr sample_rate = 48000;
4958 auto constexpr frames = 240;
4960 boost::filesystem::remove_all(path);
4961 boost::filesystem::create_directories(path);
4962 auto dcp = make_shared<dcp::DCP>(path);
4963 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
4964 cpl->set_annotation_text("hello");
4965 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R,C,LFE,Ls,Rs"));
4966 cpl->set_main_sound_sample_rate(sample_rate);
4967 cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
4968 cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
4969 cpl->set_version_number(1);
4971 auto mp = simple_picture(path, "1", frames, {});
4972 auto ms = simple_sound(path, "1", mxf_meta, "en-US", frames, sample_rate, {}, 2);
4974 auto reel = make_shared<dcp::Reel>(
4975 std::make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
4976 std::make_shared<dcp::ReelSoundAsset>(ms, 0)
4979 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), frames);
4980 markers->set(dcp::Marker::FFOC, dcp::Time(0, 0, 0, 1, 24));
4981 markers->set(dcp::Marker::LFOC, dcp::Time(0, 0, 9, 23, 24));
4987 dcp->set_annotation_text("hello");
4990 check_verify_result(
4994 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4995 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4996 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
4997 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
4998 dcp::VerificationNote(
4999 dcp::VerificationNote::Type::OK,
5000 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5001 string{"1998x1080"},
5003 ).set_cpl_id(cpl->id()),
5004 dcp::VerificationNote(
5005 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_SOUND_CONFIGURATION, std::string{"MainSoundConfiguration has 6 channels but sound assets have 2"}, canonical(find_cpl(path))
5006 ).set_cpl_id(cpl->id())
5011 BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size)
5013 boost::filesystem::path const path = "build/test/verify_invalid_tile_part_size";
5014 auto constexpr video_frames = 24;
5015 auto constexpr sample_rate = 48000;
5017 boost::filesystem::remove_all(path);
5018 boost::filesystem::create_directories(path);
5020 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE);
5021 auto picture_writer = mp->start_write(path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
5023 dcp::Size const size(1998, 1080);
5024 auto image = make_shared<dcp::OpenJPEGImage>(size);
5025 boost::random::mt19937 rng(1);
5026 boost::random::uniform_int_distribution<> dist(0, 4095);
5027 for (int c = 0; c < 3; ++c) {
5028 for (int p = 0; p < (1998 * 1080); ++p) {
5029 image->data(c)[p] = dist(rng);
5032 auto j2c = dcp::compress_j2k(image, 750000000, video_frames, false, false);
5033 for (int i = 0; i < 24; ++i) {
5034 picture_writer->write(j2c.data(), j2c.size());
5036 picture_writer->finalize();
5038 auto dcp = make_shared<dcp::DCP>(path);
5039 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
5040 cpl->set_content_version(
5041 dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11", "content-version-label-text")
5043 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R,C,LFE,Ls,Rs"));
5044 cpl->set_main_sound_sample_rate(sample_rate);
5045 cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
5046 cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
5047 cpl->set_version_number(1);
5049 auto ms = simple_sound(path, "", dcp::MXFMetadata(), "en-US", video_frames, sample_rate, {});
5051 auto reel = make_shared<dcp::Reel>(
5052 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
5053 make_shared<dcp::ReelSoundAsset>(ms, 0)
5058 dcp->set_annotation_text("A Test DCP");
5061 vector<dcp::VerificationNote> expected = {
5062 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5063 dcp::VerificationNote(
5064 dcp::VerificationNote::Type::OK,
5065 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5066 string{"1998x1080"},
5068 ).set_cpl_id(cpl->id()),
5069 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video.mxf"), cpl),
5070 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5071 dcp::VerificationNote(
5072 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC
5073 ).set_cpl_id(cpl->id()),
5074 dcp::VerificationNote(
5075 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC
5076 ).set_cpl_id(cpl->id())
5079 for (auto frame = 0; frame < 24; frame++) {
5081 dcp::VerificationNote(
5082 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(path / "video.mxf")
5083 ).set_frame(frame).set_frame_rate(24).set_cpl_id(cpl->id())
5087 int component_sizes[] = {
5093 for (auto frame = 0; frame < 24; frame++) {
5094 for (auto component = 0; component < 3; component++) {
5096 dcp::VerificationNote(
5097 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_TILE_PART_SIZE
5098 ).set_frame(frame).set_component(component).set_size(component_sizes[component]).set_cpl_id(cpl->id())
5103 check_verify_result({ path }, {}, expected);
5107 BOOST_AUTO_TEST_CASE(verify_too_many_subtitle_namespaces)
5109 boost::filesystem::path const dir = "test/ref/DCP/subtitle_namespace_test";
5112 BOOST_REQUIRE(!dcp.cpls().empty());
5113 auto cpl = dcp.cpls()[0];
5115 check_verify_result(
5119 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5120 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5121 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_42b34dcd-caa5-4c7b-aa0f-66a590947ba1.mxf"), cpl),
5122 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_42b34dcd-caa5-4c7b-aa0f-66a590947ba1.mxf"), cpl),
5123 dcp::VerificationNote(
5124 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE
5125 ).set_cpl_id(cpl->id()),
5126 dcp::VerificationNote(
5127 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE
5128 ).set_cpl_id(cpl->id()),
5129 dcp::VerificationNote(
5130 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
5131 ).set_cpl_id(cpl->id()),
5132 dcp::VerificationNote(
5133 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(find_file(dir, "sub_"))
5134 ).set_cpl_id(cpl->id()),
5135 dcp::VerificationNote(
5136 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(find_file(dir, "cpl_"))
5137 ).set_cpl_id(cpl->id()),
5138 dcp::VerificationNote(
5139 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, std::string{"315de731-1173-484c-9a35-bdacf5a9d99d"}
5140 ).set_cpl_id(cpl->id()),
5145 BOOST_AUTO_TEST_CASE(verify_missing_load_font_for_font)
5147 path const dir("build/test/verify_missing_load_font");
5148 prepare_directory (dir);
5149 copy_file ("test/data/subs1.xml", dir / "subs.xml");
5151 Editor editor(dir / "subs.xml");
5152 editor.delete_first_line_containing("LoadFont");
5154 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
5155 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
5156 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
5158 check_verify_result (
5162 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5163 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5164 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
5165 dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id("theFontId").set_cpl_id(cpl->id())
5171 BOOST_AUTO_TEST_CASE(verify_missing_load_font)
5173 boost::filesystem::path const dir = "build/test/verify_missing_load_font";
5174 prepare_directory(dir);
5175 auto dcp = make_simple (dir, 1, 202);
5178 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
5179 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
5180 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
5181 "<ContentTitleText>Content</ContentTitleText>"
5182 "<AnnotationText>Annotation</AnnotationText>"
5183 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
5184 "<ReelNumber>1</ReelNumber>"
5185 "<EditRate>24 1</EditRate>"
5186 "<TimeCodeRate>24</TimeCodeRate>"
5187 "<StartTime>00:00:00:00</StartTime>"
5188 "<Language>de-DE</Language>"
5190 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
5191 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:06:00\" TimeOut=\"00:00:08:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
5192 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
5198 dcp::File xml_file(dir / "subs.xml", "w");
5199 BOOST_REQUIRE(xml_file);
5200 xml_file.write(xml.c_str(), xml.size(), 1);
5202 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
5203 subs->write(dir / "subs.mxf");
5205 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 202, 0);
5206 auto cpl = dcp->cpls()[0];
5207 cpl->reels()[0]->add(reel_subs);
5210 check_verify_result (
5214 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5215 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5216 dcp::VerificationNote(
5217 dcp::VerificationNote::Type::OK,
5218 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5219 string{"1998x1080"},
5221 ).set_cpl_id(cpl->id()),
5222 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
5223 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
5224 dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT).set_id(reel_subs->id()).set_cpl_id(cpl->id())
5229 BOOST_AUTO_TEST_CASE(verify_spots_wrong_asset)
5231 boost::filesystem::path const dir = "build/test/verify_spots_wrong_asset";
5232 boost::filesystem::remove_all(dir);
5234 auto dcp1 = make_simple(dir / "1");
5236 auto cpl = dcp1->cpls()[0];
5238 auto const asset_1 = dcp::MonoPictureAsset(dir / "1" / "video.mxf").id();
5240 auto dcp2 = make_simple(dir / "2");
5242 auto const asset_2 = dcp::MonoPictureAsset(dir / "2" / "video.mxf").id();
5244 boost::filesystem::remove(dir / "1" / "video.mxf");
5245 boost::filesystem::copy_file(dir / "2" / "video.mxf", dir / "1" / "video.mxf");
5247 check_verify_result(
5251 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5252 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5253 dcp::VerificationNote(
5254 dcp::VerificationNote::Type::OK,
5255 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5256 string{"1998x1080"},
5258 ).set_cpl_id(cpl->id()),
5259 dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_MAP_ID).set_id(asset_1).set_other_id(asset_2)
5264 BOOST_AUTO_TEST_CASE(verify_cpl_content_version_label_text_empty)
5266 boost::filesystem::path const dir = "build/test/verify_cpl_content_version_label_text_empty";
5267 boost::filesystem::remove_all(dir);
5269 auto dcp = make_simple(dir);
5270 BOOST_REQUIRE(dcp->cpls().size() == 1);
5271 auto cpl = dcp->cpls()[0];
5272 cpl->set_content_version(dcp::ContentVersion(""));
5275 check_verify_result(
5279 dcp::VerificationNote(
5280 dcp::VerificationNote::Type::OK,
5281 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5282 string{"1998x1080"},
5284 ).set_cpl_id(cpl->id()),
5285 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5286 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5287 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
5288 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
5289 dcp::VerificationNote(dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()).set_cpl_id(cpl->id())
5294 /** Check that we don't get any strange errors when verifying encrypted DCPs (DoM #2659) */
5295 BOOST_AUTO_TEST_CASE(verify_encrypted_smpte_dcp)
5297 auto const dir = path("build/test/verify_encrypted_smpte_dcp");
5299 auto key_id = dcp::make_uuid();
5300 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset>(dir, {{ 4 * 24, 5 * 24 }}, key, key_id);
5302 dcp::DecryptedKDM kdm(dcp::LocalTime(), dcp::LocalTime(), "", "", "");
5303 kdm.add_key(dcp::DecryptedKDMKey(string{"MDIK"}, key_id, key, cpl->id(), dcp::Standard::SMPTE));
5305 path const pkl_file = find_file(dir, "pkl_");
5306 path const cpl_file = find_file(dir, "cpl_");
5308 check_verify_result(
5312 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5313 ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl),
5314 dcp::VerificationNote(
5315 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_file)
5316 ).set_cpl_id(cpl->id()),
5317 dcp::VerificationNote(
5318 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, canonical(cpl_file)
5319 ).set_cpl_id(cpl->id()),
5320 dcp::VerificationNote(
5321 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, filename_to_id(pkl_file.filename()), canonical(pkl_file)