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, string note, shared_ptr<const dcp::CPL> cpl)
369 return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code, note).set_cpl_id(cpl->id());
374 dcp::VerificationNote
375 ok(dcp::VerificationNote::Code code, boost::filesystem::path path, shared_ptr<const dcp::CPL> cpl)
377 return dcp::VerificationNote(dcp::VerificationNote::Type::OK, code, path).set_cpl_id(cpl->id());
382 add(vector<dcp::VerificationNote>& notes, vector<dcp::VerificationNote> const& add)
390 BOOST_AUTO_TEST_CASE (verify_no_error)
393 auto dir = setup (1, "no_error");
394 auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes;
396 path const cpl_file = dir / dcp_test1_cpl();
397 path const pkl_file = dir / dcp_test1_pkl();
398 path const assetmap_file = dir / "ASSETMAP.xml";
400 auto st = stages.begin();
401 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
402 BOOST_REQUIRE (st->second);
403 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir));
405 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
406 BOOST_REQUIRE (st->second);
407 BOOST_CHECK_EQUAL (st->second.get(), canonical(cpl_file));
409 BOOST_CHECK_EQUAL (st->first, "Checking reel");
410 BOOST_REQUIRE (!st->second);
412 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
413 BOOST_REQUIRE (st->second);
414 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "video.mxf"));
416 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
417 BOOST_REQUIRE (st->second);
418 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "video.mxf"));
420 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
421 BOOST_REQUIRE (st->second);
422 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "audio.mxf"));
424 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
425 BOOST_REQUIRE (st->second);
426 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "audio.mxf"));
428 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
429 BOOST_REQUIRE (st->second);
430 BOOST_CHECK_EQUAL (st->second.get(), canonical(pkl_file));
431 ++st; BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
432 BOOST_REQUIRE (st->second);
433 BOOST_CHECK_EQUAL (st->second.get(), canonical(assetmap_file));
435 BOOST_REQUIRE (st == stages.end());
437 for (auto note: notes) {
438 BOOST_CHECK(note.type() == dcp::VerificationNote::Type::OK);
443 BOOST_AUTO_TEST_CASE (verify_incorrect_picture_sound_hash)
445 using namespace boost::filesystem;
447 auto dir = setup (1, "incorrect_picture_sound_hash");
448 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
450 auto video_path = path(dir / "video.mxf");
451 HashCalculator video_calc(video_path);
452 auto mod = fopen(video_path.string().c_str(), "r+b");
454 BOOST_REQUIRE_EQUAL(fseek(mod, -16, SEEK_END), 0);
456 BOOST_REQUIRE(fwrite(&x, sizeof(x), 1, mod) == 1);
459 auto audio_path = path(dir / "audio.mxf");
460 HashCalculator audio_calc(audio_path);
461 mod = fopen(audio_path.string().c_str(), "r+b");
463 BOOST_REQUIRE_EQUAL(fseek(mod, 0, SEEK_END), 0);
464 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
467 dcp::ASDCPErrorSuspender sus;
468 check_verify_result (
472 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
473 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
474 dcp::VerificationNote(
475 dcp::VerificationNote::Type::OK,
476 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
478 canonical(cpl->file().get())
479 ).set_cpl_id(dcp_test1_cpl_id()),
480 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
481 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
482 dcp::VerificationNote(
483 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_PICTURE_HASH, canonical(video_path)
484 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(video_calc.old_hash()).set_calculated_hash(video_calc.new_hash()),
485 dcp::VerificationNote(
486 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_SOUND_HASH, canonical(audio_path)
487 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(audio_calc.old_hash()).set_calculated_hash(audio_calc.new_hash()),
492 BOOST_AUTO_TEST_CASE (verify_mismatched_picture_sound_hashes)
494 using namespace boost::filesystem;
496 auto dir = setup (1, "mismatched_picture_sound_hashes");
497 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
499 HashCalculator calc(dir / dcp_test1_cpl());
502 Editor e (dir / dcp_test1_pkl());
503 e.replace ("<Hash>", "<Hash>x");
506 check_verify_result (
510 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
511 dcp::VerificationNote(
512 dcp::VerificationNote::Type::OK,
513 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
515 canonical(cpl->file().get())
516 ).set_cpl_id(cpl->id()),
517 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
518 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
519 dcp::VerificationNote(
520 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(dir / dcp_test1_cpl())
521 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash("x" + calc.old_hash()).set_calculated_hash(calc.old_hash()),
522 dcp::VerificationNote(
523 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES, canonical(dir / "video.mxf")
524 ).set_cpl_id(dcp_test1_cpl_id()),
525 dcp::VerificationNote(
526 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES, canonical(dir / "audio.mxf")
527 ).set_cpl_id(dcp_test1_cpl_id()),
528 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'x3M7YTgvFKXXMEGLkIbV4miC90FE=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 28 },
529 { 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 },
530 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xvsVjRV9vhTBPUWfE/TT1o2vdQsI=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl()), 20 },
535 BOOST_AUTO_TEST_CASE (verify_failed_read_content_kind)
537 auto dir = setup (1, "failed_read_content_kind");
539 HashCalculator calc(dir / dcp_test1_cpl());
542 Editor e (dir / dcp_test1_cpl());
543 e.replace ("<ContentKind>", "<ContentKind>x");
546 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
548 check_verify_result (
552 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
553 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
554 dcp::VerificationNote(
555 dcp::VerificationNote::Type::OK,
556 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
558 canonical(cpl->file().get())
559 ).set_cpl_id(cpl->id()),
560 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
561 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
562 dcp::VerificationNote(
563 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(dir / dcp_test1_cpl())
564 ).set_cpl_id(dcp_test1_cpl_id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
565 dcp::VerificationNote(
566 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("xtrailer")
567 ).set_cpl_id(dcp_test1_cpl_id())
574 dcp_test1_cpl_path(string suffix)
576 return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_cpl());
582 dcp_test1_pkl_path(string suffix)
584 return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_pkl());
590 asset_map (string suffix)
592 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", suffix);
596 BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_rate)
598 auto const suffix = "invalid_picture_frame_rate";
600 replace(suffix, &dcp_test1_cpl_path, "<FrameRate>24 1", "<FrameRate>99 1");
602 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
603 auto const cpl_path = find_cpl(dir);
604 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
606 std::vector<dcp::VerificationNote> expected =
608 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
609 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
610 dcp::VerificationNote(
611 dcp::VerificationNote::Type::OK,
612 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
614 canonical(cpl->file().get())
615 ).set_cpl_id(cpl->id()),
616 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
617 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
618 dcp::VerificationNote(
619 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
620 ).set_cpl_id(cpl->id()).set_calculated_hash("7n7GQ2TbxQbmHYuAR8ml7XDOep8=").set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI="),
621 dcp::VerificationNote(
622 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, string{"99/1"}
623 ).set_cpl_id(cpl->id())
626 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
629 BOOST_AUTO_TEST_CASE (verify_missing_asset)
631 auto dir = setup (1, "missing_asset");
632 remove (dir / "video.mxf");
634 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
636 check_verify_result (
640 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
641 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
642 dcp::VerificationNote(
643 dcp::VerificationNote::Type::OK,
644 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
646 canonical(cpl->file().get())
647 ).set_cpl_id(cpl->id()),
648 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
649 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_ASSET, canonical(dir) / "video.mxf" }
654 BOOST_AUTO_TEST_CASE (verify_empty_asset_path)
656 auto const suffix = "empty_asset_path";
658 replace("empty_asset_path", &asset_map, "<Path>video.mxf</Path>", "<Path></Path>");
660 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
661 auto const cpl_path = find_cpl(dir);
662 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
664 std::vector<dcp::VerificationNote> expected = {
665 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
666 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
667 dcp::VerificationNote(
668 dcp::VerificationNote::Type::OK,
669 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
671 canonical(cpl->file().get())
672 ).set_cpl_id(cpl->id()),
673 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
674 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_ASSET_PATH }
677 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
681 BOOST_AUTO_TEST_CASE (verify_mismatched_standard)
683 auto const suffix = "mismatched_standard";
685 replace(suffix, &dcp_test1_cpl_path, "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#");
687 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
688 auto const cpl_path = find_cpl(dir);
689 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
691 std::vector<dcp::VerificationNote> expected = {
692 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
693 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
694 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
695 dcp::VerificationNote(
696 dcp::VerificationNote::Type::OK,
697 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
699 canonical(cpl->file().get())
700 ).set_cpl_id(cpl->id()),
701 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
702 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_STANDARD },
703 dcp::VerificationNote(
704 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "invalid character encountered", canonical(cpl_path), 42
705 ).set_cpl_id(cpl->id()),
706 dcp::VerificationNote(
707 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'Id'", canonical(cpl_path), 53
708 ).set_cpl_id(cpl->id()),
709 dcp::VerificationNote(
710 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'EditRate'", canonical(cpl_path), 54
711 ).set_cpl_id(cpl->id()),
712 dcp::VerificationNote(
713 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "no declaration found for element 'IntrinsicDuration'", canonical(cpl_path), 55
714 ).set_cpl_id(cpl->id()),
715 dcp::VerificationNote(
716 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
717 "element 'Id' is not allowed for content model '(Id,AnnotationText?,EditRate,IntrinsicDuration,"
718 "EntryPoint?,Duration?,FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
719 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,MainSoundSampleRate,"
720 "MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,ExtensionMetadataList?,)'",
721 canonical(cpl_path), 149
722 ).set_cpl_id(cpl->id()),
723 dcp::VerificationNote(
724 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
725 ).set_cpl_id(cpl->id()).set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI=").set_calculated_hash("FZ9E7L/pOuJ6aZfbiaANTv8BFOo=")
728 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
732 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_id)
734 auto const suffix = "invalid_xml_cpl_id";
736 /* There's no MISMATCHED_CPL_HASHES error here because it can't find the correct hash by ID (since the ID is wrong) */
737 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");
739 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
740 auto const cpl_path = find_cpl(dir);
741 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
743 std::vector<dcp::VerificationNote> expected = {
744 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
745 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
746 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
747 dcp::VerificationNote(
748 dcp::VerificationNote::Type::OK,
749 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
751 canonical(cpl->file().get())
752 ).set_cpl_id(cpl->id()),
753 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
754 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
755 dcp::VerificationNote(
756 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
757 "value 'urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a' does not match regular expression "
758 "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
759 ).set_cpl_id(cpl->id())
762 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
766 BOOST_AUTO_TEST_CASE (verify_invalid_xml_issue_date)
768 auto const suffix = "invalid_xml_issue_date";
770 replace("invalid_xml_issue_date", &dcp_test1_cpl_path, "<IssueDate>", "<IssueDate>x");
772 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
773 auto const cpl_path = find_cpl(dir);
774 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
776 std::vector<dcp::VerificationNote> expected = {
777 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
778 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
779 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
780 dcp::VerificationNote(
781 dcp::VerificationNote::Type::OK,
782 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
784 canonical(cpl->file().get())
785 ).set_cpl_id(cpl->id()),
786 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
787 dcp::VerificationNote(
788 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
789 ).set_cpl_id(cpl->id()).set_reference_hash("skI+5b/9LA/y6h0mcyxysJYanxI=").set_calculated_hash("sz3BeIugJ567q3HMnA62JeRw4TE="),
790 dcp::VerificationNote(
791 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
792 "invalid character encountered",
793 canonical(cpl_path), 5
794 ).set_cpl_id(cpl->id()),
797 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
801 BOOST_AUTO_TEST_CASE (verify_invalid_xml_pkl_id)
803 auto const suffix = "invalid_xml_pkl_id";
805 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));
807 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
808 auto const pkl_path = find_pkl(dir);
809 auto const cpl_path = find_cpl(dir);
810 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
812 std::vector<dcp::VerificationNote> expected = {
813 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
814 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
815 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
816 dcp::VerificationNote(
817 dcp::VerificationNote::Type::OK,
818 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
820 canonical(cpl->file().get())
821 ).set_cpl_id(cpl->id()),
822 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
823 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
824 dcp::VerificationNote(
825 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
826 "value 'urn:uuid:x199d58b-5ef8-4d49-b270-07e590ccb280' does not match regular "
827 "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}'",
828 canonical(pkl_path), 3
832 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
836 BOOST_AUTO_TEST_CASE (verify_invalid_xml_asset_map_id)
838 auto const suffix = "invalid_xml_asset_map_id";
840 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));
842 auto const dir = dcp::String::compose("build/test/verify_test%1", suffix);
843 auto const cpl_path = find_cpl(dir);
844 auto const asset_map_path = find_asset_map(dir);
845 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
847 std::vector<dcp::VerificationNote> expected = {
848 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
849 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
850 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
851 dcp::VerificationNote(
852 dcp::VerificationNote::Type::OK,
853 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
855 canonical(cpl->file().get())
856 ).set_cpl_id(cpl->id()),
857 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
858 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
859 dcp::VerificationNote(
860 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML,
861 "value 'urn:uuid:x17b3de4-6dda-408d-b19b-6711354b0bc3' does not match regular "
862 "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}'",
863 canonical(asset_map_path), 3
867 check_verify_result(dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes, expected);
871 BOOST_AUTO_TEST_CASE (verify_invalid_standard)
874 auto dir = setup (3, "verify_invalid_standard");
875 auto notes = dcp::verify({dir}, {}, &stage, &progress, {}, xsd_test).notes;
877 path const cpl_file = dir / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
878 path const pkl_file = dir / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
879 path const assetmap_file = dir / "ASSETMAP";
880 auto cpl = std::make_shared<dcp::CPL>(cpl_file);
882 auto st = stages.begin();
883 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
884 BOOST_REQUIRE (st->second);
885 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir));
887 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
888 BOOST_REQUIRE (st->second);
889 BOOST_CHECK_EQUAL (st->second.get(), canonical(cpl_file));
891 BOOST_CHECK_EQUAL (st->first, "Checking reel");
892 BOOST_REQUIRE (!st->second);
894 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
895 BOOST_REQUIRE (st->second);
896 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
898 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
899 BOOST_REQUIRE (st->second);
900 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
902 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
903 BOOST_REQUIRE (st->second);
904 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
906 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
907 BOOST_REQUIRE (st->second);
908 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
910 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
911 BOOST_REQUIRE (st->second);
912 BOOST_CHECK_EQUAL (st->second.get(), canonical(pkl_file));
914 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
915 BOOST_REQUIRE (st->second);
916 BOOST_CHECK_EQUAL (st->second.get(), canonical(assetmap_file));
918 BOOST_REQUIRE (st == stages.end());
920 vector<dcp::VerificationNote> expected = {
921 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
922 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
923 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
924 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"), cpl),
925 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"), cpl)
928 for (int j = 0; j < 24; ++j) {
930 dcp::VerificationNote(
931 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2")
932 ).set_cpl_id(cpl->id())
936 check_verify_result(notes, expected);
939 /* DCP with a short asset */
940 BOOST_AUTO_TEST_CASE (verify_invalid_duration)
942 auto dir = setup (8, "invalid_duration");
946 BOOST_REQUIRE(dcp.cpls().size() == 1);
947 auto cpl = dcp.cpls()[0];
949 vector<dcp::VerificationNote> expected = {
950 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
951 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
952 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_d7576dcb-a361-4139-96b8-267f5f8d7f91.mxf"), cpl),
953 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_d7576dcb-a361-4139-96b8-267f5f8d7f91.mxf"), cpl),
954 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
955 dcp::VerificationNote(
956 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91")
957 ).set_cpl_id(cpl->id()),
958 dcp::VerificationNote(
959 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91")
960 ).set_cpl_id(cpl->id()),
961 dcp::VerificationNote(
962 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626")
963 ).set_cpl_id(cpl->id()),
964 dcp::VerificationNote(
965 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626")
966 ).set_cpl_id(cpl->id()),
967 dcp::VerificationNote(
968 dcp::VerificationNote::Type::WARNING,
969 dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT,
971 ).set_cpl_id(cpl->id())
974 for (int i = 0; i < 23; ++i) {
976 dcp::VerificationNote(
977 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2")
978 ).set_cpl_id(cpl->id())
982 check_verify_result({ dir }, {}, expected);
988 dcp_from_frame (dcp::ArrayData const& frame, path dir)
990 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE);
991 create_directories (dir);
992 auto writer = asset->start_write(dir / "pic.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
993 for (int i = 0; i < 24; ++i) {
994 writer->write (frame.data(), frame.size());
998 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
999 return write_dcp_with_single_asset (dir, reel_asset);
1003 BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_size_in_bytes)
1005 int const too_big = 1302083 * 2;
1007 /* Compress a black image */
1008 auto image = black_image ();
1009 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1010 BOOST_REQUIRE (frame.size() < too_big);
1012 /* Place it in a bigger block with some zero padding at the end */
1013 dcp::ArrayData oversized_frame(too_big);
1014 memcpy (oversized_frame.data(), frame.data(), frame.size());
1015 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
1017 path const dir("build/test/verify_invalid_picture_frame_size_in_bytes");
1018 prepare_directory (dir);
1019 auto cpl = dcp_from_frame (oversized_frame, dir);
1021 vector<dcp::VerificationNote> expected = {
1022 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1023 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1024 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1025 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1028 for (auto i = 0; i < 24; ++i) {
1030 dcp::VerificationNote(
1031 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte")
1032 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1036 for (auto i = 0; i < 24; ++i) {
1038 dcp::VerificationNote(
1039 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf")
1040 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1045 dcp::VerificationNote(
1046 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1047 ).set_cpl_id(cpl->id())
1050 check_verify_result({ dir }, {}, expected);
1054 BOOST_AUTO_TEST_CASE (verify_nearly_invalid_picture_frame_size_in_bytes)
1056 int const nearly_too_big = 1302083 * 0.98;
1058 /* Compress a black image */
1059 auto image = black_image ();
1060 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1061 BOOST_REQUIRE (frame.size() < nearly_too_big);
1063 /* Place it in a bigger block with some zero padding at the end */
1064 dcp::ArrayData oversized_frame(nearly_too_big);
1065 memcpy (oversized_frame.data(), frame.data(), frame.size());
1066 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
1068 path const dir("build/test/verify_nearly_invalid_picture_frame_size_in_bytes");
1069 prepare_directory (dir);
1070 auto cpl = dcp_from_frame (oversized_frame, dir);
1072 vector<dcp::VerificationNote> expected = {
1073 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1074 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1075 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1076 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1079 for (auto i = 0; i < 24; ++i) {
1081 dcp::VerificationNote(
1082 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte")
1083 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1087 for (auto i = 0; i < 24; ++i) {
1089 dcp::VerificationNote(
1090 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf")
1091 ).set_frame(i).set_frame_rate(24).set_cpl_id(cpl->id())
1096 dcp::VerificationNote(
1097 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1098 ).set_cpl_id(cpl->id())
1101 check_verify_result ({ dir }, {}, expected);
1105 BOOST_AUTO_TEST_CASE (verify_valid_picture_frame_size_in_bytes)
1107 /* Compress a black image */
1108 auto image = black_image ();
1109 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1110 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
1112 path const dir("build/test/verify_valid_picture_frame_size_in_bytes");
1113 prepare_directory (dir);
1114 auto cpl = dcp_from_frame (frame, dir);
1116 check_verify_result(
1120 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1121 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1122 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1123 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1124 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "pic.mxf"), cpl),
1125 dcp::VerificationNote(dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()).set_cpl_id(cpl->id())
1130 BOOST_AUTO_TEST_CASE (verify_valid_interop_subtitles)
1132 path const dir("build/test/verify_valid_interop_subtitles");
1133 prepare_directory (dir);
1134 copy_file ("test/data/subs1.xml", dir / "subs.xml");
1135 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1136 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1137 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1139 check_verify_result (
1143 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1144 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1145 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1146 dcp::VerificationNote(
1147 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1148 ).set_cpl_id(cpl->id())
1153 BOOST_AUTO_TEST_CASE(verify_catch_missing_font_file_with_interop_ccap)
1155 path const dir("build/test/verify_catch_missing_font_file_with_interop_ccap");
1156 prepare_directory(dir);
1157 copy_file("test/data/subs1.xml", dir / "ccap.xml");
1158 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "ccap.xml");
1159 auto reel_asset = make_shared<dcp::ReelInteropClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1160 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1162 check_verify_result (
1166 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1167 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1168 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1169 dcp::VerificationNote(
1170 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1171 ).set_cpl_id(cpl->id())
1176 BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles)
1178 using namespace boost::filesystem;
1180 path const dir("build/test/verify_invalid_interop_subtitles");
1181 prepare_directory (dir);
1182 copy_file ("test/data/subs1.xml", dir / "subs.xml");
1183 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1184 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1185 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1188 Editor e (dir / "subs.xml");
1189 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
1192 check_verify_result (
1196 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1197 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1198 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1199 dcp::VerificationNote(
1200 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 5
1201 ).set_cpl_id(cpl->id()),
1202 dcp::VerificationNote(
1203 dcp::VerificationNote::Type::ERROR,
1204 dcp::VerificationNote::Code::INVALID_XML,
1205 string("element 'Foo' is not allowed for content model '(SubtitleID,MovieTitle,ReelNumber,Language,LoadFont*,Font*,Subtitle*)'"),
1208 ).set_cpl_id(cpl->id()),
1209 dcp::VerificationNote(
1210 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1211 ).set_cpl_id(cpl->id())
1216 BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_no_subtitles)
1218 path const dir("build/test/verify_interop_subtitle_asset_with_no_subtitles");
1219 prepare_directory(dir);
1220 copy_file("test/data/subs4.xml", dir / "subs.xml");
1221 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1222 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1223 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1225 check_verify_result (
1229 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1230 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1231 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1232 dcp::VerificationNote(
1233 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get())
1234 ).set_cpl_id(cpl->id()),
1235 dcp::VerificationNote(
1236 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"theFontId"}
1237 ).set_cpl_id(cpl->id())
1243 BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_single_space_subtitle)
1245 path const dir("build/test/verify_interop_subtitle_asset_with_single_space_subtitle");
1246 prepare_directory(dir);
1247 copy_file("test/data/subs5.xml", dir / "subs.xml");
1248 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1249 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1250 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
1252 check_verify_result (
1256 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1257 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1258 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1259 dcp::VerificationNote(
1260 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"Arial"}
1261 ).set_cpl_id(cpl->id())
1267 BOOST_AUTO_TEST_CASE (verify_valid_smpte_subtitles)
1269 path const dir("build/test/verify_valid_smpte_subtitles");
1270 prepare_directory (dir);
1271 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1272 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1273 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1274 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1276 check_verify_result(
1280 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1281 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1282 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1283 dcp::VerificationNote(
1284 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1285 ).set_cpl_id(cpl->id()),
1286 dcp::VerificationNote(
1287 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-04-14T13:19:14.000+02:00"}
1288 ).set_cpl_id(cpl->id()),
1289 dcp::VerificationNote(
1290 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()
1291 ).set_cpl_id(cpl->id()),
1296 BOOST_AUTO_TEST_CASE (verify_invalid_smpte_subtitles)
1298 using namespace boost::filesystem;
1300 path const dir("build/test/verify_invalid_smpte_subtitles");
1301 prepare_directory (dir);
1302 /* This broken_smpte.mxf does not use urn:uuid: for its subtitle ID, which we tolerate (rightly or wrongly) */
1303 copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
1304 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1305 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1306 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1308 check_verify_result (
1312 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1313 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1314 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1315 dcp::VerificationNote(
1316 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 2
1317 ).set_cpl_id(cpl->id()),
1318 dcp::VerificationNote(
1319 dcp::VerificationNote::Type::ERROR,
1320 dcp::VerificationNote::Code::INVALID_XML,
1321 string("element 'Foo' is not allowed for content model '(Id,ContentTitleText,AnnotationText?,IssueDate,ReelNumber?,Language?,EditRate,TimeCodeRate,StartTime?,DisplayType?,LoadFont*,SubtitleList)'"),
1324 ).set_cpl_id(cpl->id()),
1325 dcp::VerificationNote(
1326 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
1327 ).set_cpl_id(cpl->id()),
1328 dcp::VerificationNote(
1329 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1330 ).set_cpl_id(cpl->id()),
1331 dcp::VerificationNote(
1332 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2020-05-09T00:29:21.000+02:00"}
1333 ).set_cpl_id(cpl->id()),
1334 dcp::VerificationNote(
1335 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()
1336 ).set_cpl_id(cpl->id()),
1341 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles)
1343 path const dir("build/test/verify_empty_text_node_in_subtitles");
1344 prepare_directory (dir);
1345 copy_file ("test/data/empty_text.mxf", dir / "subs.mxf");
1346 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1347 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
1348 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1350 check_verify_result (
1354 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1355 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1356 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1357 dcp::VerificationNote(
1358 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT
1359 ).set_cpl_id(cpl->id()),
1360 dcp::VerificationNote(
1361 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
1362 ).set_cpl_id(cpl->id()),
1363 dcp::VerificationNote(
1364 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf")
1365 ).set_cpl_id(cpl->id()),
1366 dcp::VerificationNote(
1367 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1368 ).set_cpl_id(cpl->id()),
1369 dcp::VerificationNote(
1370 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"}
1371 ).set_cpl_id(cpl->id()),
1372 dcp::VerificationNote(
1373 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, asset->id()
1374 ).set_cpl_id(cpl->id())
1379 /** A <Text> node with no content except some <Font> nodes, which themselves do have content */
1380 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes)
1382 path const dir("build/test/verify_empty_text_node_in_subtitles_with_child_nodes");
1383 prepare_directory (dir);
1384 copy_file ("test/data/empty_but_with_children.xml", dir / "subs.xml");
1385 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1386 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
1387 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
1389 check_verify_result (
1393 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1394 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1395 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1396 dcp::VerificationNote(
1397 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"}
1398 ).set_cpl_id(cpl->id())
1403 /** A <Text> node with no content except some <Font> nodes, which themselves also have no content */
1404 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes)
1406 path const dir("build/test/verify_empty_text_node_in_subtitles_with_empty_child_nodes");
1407 prepare_directory (dir);
1408 copy_file ("test/data/empty_with_empty_children.xml", dir / "subs.xml");
1409 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
1410 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
1411 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
1413 check_verify_result (
1417 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1418 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1419 dcp::VerificationNote(
1420 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get())
1421 ).set_cpl_id(cpl->id()),
1422 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
1423 dcp::VerificationNote(
1424 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT
1425 ).set_cpl_id(cpl->id()),
1426 dcp::VerificationNote(
1427 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_FONT, string{"font0"}
1428 ).set_cpl_id(cpl->id())
1433 BOOST_AUTO_TEST_CASE (verify_external_asset)
1435 path const ov_dir("build/test/verify_external_asset");
1436 prepare_directory (ov_dir);
1438 auto image = black_image ();
1439 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
1440 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
1441 dcp_from_frame (frame, ov_dir);
1443 dcp::DCP ov (ov_dir);
1446 path const vf_dir("build/test/verify_external_asset_vf");
1447 prepare_directory (vf_dir);
1449 auto picture = ov.cpls()[0]->reels()[0]->main_picture();
1450 auto cpl = write_dcp_with_single_asset (vf_dir, picture);
1452 check_verify_result (
1456 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1457 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1458 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1459 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, picture->asset()->id() },
1460 dcp::VerificationNote(
1461 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1462 ).set_cpl_id(cpl->id())
1467 BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata)
1469 path const dir("build/test/verify_valid_cpl_metadata");
1470 prepare_directory (dir);
1472 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1473 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1474 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1476 auto reel = make_shared<dcp::Reel>();
1477 reel->add (reel_asset);
1479 reel->add (make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 16 * 24), 0));
1480 reel->add (simple_markers(16 * 24));
1482 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1484 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1485 cpl->set_main_sound_sample_rate (48000);
1486 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1487 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1488 cpl->set_version_number (1);
1492 dcp.set_annotation_text("hello");
1497 /* DCP with invalid CompositionMetadataAsset */
1498 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag)
1500 using namespace boost::filesystem;
1502 path const dir("build/test/verify_invalid_cpl_metadata_bad_tag");
1503 prepare_directory (dir);
1505 auto reel = make_shared<dcp::Reel>();
1506 reel->add (black_picture_asset(dir));
1507 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1509 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1510 cpl->set_main_sound_sample_rate (48000);
1511 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1512 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1513 cpl->set_version_number (1);
1515 reel->add (simple_markers());
1519 dcp.set_annotation_text("hello");
1522 HashCalculator calc(find_cpl(dir));
1525 Editor e (find_cpl(dir));
1526 e.replace ("MainSound", "MainSoundX");
1529 check_verify_result (
1533 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "pic.mxf"), cpl),
1534 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1535 dcp::VerificationNote(
1536 dcp::VerificationNote::Type::OK,
1537 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1538 string{"1440x1080"},
1540 ).set_cpl_id(cpl->id()),
1541 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1542 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "pic.mxf"), cpl),
1543 dcp::VerificationNote(
1544 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50
1545 ).set_cpl_id(cpl->id()),
1546 dcp::VerificationNote(
1547 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51
1548 ).set_cpl_id(cpl->id()),
1549 dcp::VerificationNote(
1550 dcp::VerificationNote::Type::ERROR,
1551 dcp::VerificationNote::Code::INVALID_XML,
1552 string("element 'meta:MainSoundXConfiguration' is not allowed for content model "
1553 "'(Id,AnnotationText?,EditRate,IntrinsicDuration,EntryPoint?,Duration?,"
1554 "FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
1555 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,"
1556 "MainSoundSampleRate,MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,"
1557 "ExtensionMetadataList?,)'"),
1558 canonical(cpl->file().get()),
1559 71).set_cpl_id(cpl->id()),
1560 dcp::VerificationNote(
1561 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get())
1562 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash())
1567 /* DCP with invalid CompositionMetadataAsset */
1568 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_missing_tag)
1570 path const dir("build/test/verify_invalid_cpl_metadata_missing_tag");
1571 prepare_directory (dir);
1573 auto reel = make_shared<dcp::Reel>();
1574 reel->add (black_picture_asset(dir));
1575 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1577 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1578 cpl->set_main_sound_sample_rate (48000);
1579 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1580 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1584 dcp.set_annotation_text("hello");
1588 Editor e (find_cpl(dir));
1589 e.replace ("meta:Width", "meta:WidthX");
1592 check_verify_result (
1595 {{ dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::FAILED_READ, string("missing XML tag Width in MainPictureStoredArea") }}
1600 BOOST_AUTO_TEST_CASE (verify_invalid_language1)
1602 path const dir("build/test/verify_invalid_language1");
1603 prepare_directory (dir);
1604 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1605 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1606 asset->_language = "wrong-andbad";
1607 asset->write (dir / "subs.mxf");
1608 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1609 reel_asset->_language = "badlang";
1610 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1612 check_verify_result (
1616 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1617 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1618 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1619 dcp::VerificationNote(
1620 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang")
1621 ).set_cpl_id(cpl->id()),
1622 dcp::VerificationNote(
1623 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad")
1624 ).set_cpl_id(cpl->id()),
1625 dcp::VerificationNote(
1626 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1627 ).set_cpl_id(cpl->id())
1632 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
1633 BOOST_AUTO_TEST_CASE (verify_invalid_language2)
1635 path const dir("build/test/verify_invalid_language2");
1636 prepare_directory (dir);
1637 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1638 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1639 asset->_language = "wrong-andbad";
1640 asset->write (dir / "subs.mxf");
1641 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1642 reel_asset->_language = "badlang";
1643 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1645 check_verify_result (
1649 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1650 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1651 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1652 dcp::VerificationNote(
1653 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang")
1654 ).set_cpl_id(cpl->id()),
1655 dcp::VerificationNote(
1656 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad")
1657 ).set_cpl_id(cpl->id()),
1658 dcp::VerificationNote(
1659 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
1660 ).set_cpl_id(cpl->id())
1665 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
1666 * the release territory.
1668 BOOST_AUTO_TEST_CASE (verify_invalid_language3)
1670 path const dir("build/test/verify_invalid_language3");
1671 prepare_directory (dir);
1673 auto picture = simple_picture (dir, "foo");
1674 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1675 auto reel = make_shared<dcp::Reel>();
1676 reel->add (reel_picture);
1677 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
1678 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1679 reel->add (reel_sound);
1680 reel->add (simple_markers());
1682 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1684 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
1685 cpl->_additional_subtitle_languages.push_back("andso-is-this");
1686 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1687 cpl->set_main_sound_sample_rate (48000);
1688 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1689 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1690 cpl->set_version_number (1);
1691 cpl->_release_territory = "fred-jim";
1692 auto dcp = make_shared<dcp::DCP>(dir);
1694 dcp->set_annotation_text("hello");
1697 check_verify_result (
1701 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "videofoo.mxf"), cpl),
1702 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1703 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1704 dcp::VerificationNote(
1705 dcp::VerificationNote::Type::OK,
1706 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1707 string{"1440x1080"},
1709 ).set_cpl_id(cpl->id()),
1710 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
1711 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "videofoo.mxf"), cpl),
1712 dcp::VerificationNote(
1713 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong")
1714 ).set_cpl_id(cpl->id()),
1715 dcp::VerificationNote(
1716 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this")
1717 ).set_cpl_id(cpl->id()),
1718 dcp::VerificationNote(
1719 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim")
1720 ).set_cpl_id(cpl->id()),
1721 dcp::VerificationNote(
1722 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz")
1723 ).set_cpl_id(cpl->id()),
1729 std::tuple<vector<dcp::VerificationNote>, shared_ptr<dcp::CPL>, boost::filesystem::path>
1730 check_picture_size (int width, int height, int frame_rate, bool three_d)
1732 using namespace boost::filesystem;
1734 path dcp_path = "build/test/verify_picture_test";
1735 prepare_directory (dcp_path);
1737 shared_ptr<dcp::PictureAsset> mp;
1739 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1741 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1743 auto picture_writer = mp->start_write(dcp_path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
1745 auto image = black_image (dcp::Size(width, height));
1746 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1747 int const length = three_d ? frame_rate * 2 : frame_rate;
1748 for (int i = 0; i < length; ++i) {
1749 picture_writer->write (j2c.data(), j2c.size());
1751 picture_writer->finalize ();
1753 auto d = make_shared<dcp::DCP>(dcp_path);
1754 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1755 cpl->set_annotation_text ("A Test DCP");
1756 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1757 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
1758 cpl->set_main_sound_sample_rate (48000);
1759 cpl->set_main_picture_stored_area(dcp::Size(width, height));
1760 cpl->set_main_picture_active_area(dcp::Size(width, height));
1761 cpl->set_version_number (1);
1763 auto reel = make_shared<dcp::Reel>();
1766 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1768 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1771 reel->add (simple_markers(frame_rate));
1776 d->set_annotation_text("A Test DCP");
1779 /* It seems that for the Ubuntu 16.04 compiler we can't use an initializer list here */
1780 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 };
1786 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1788 vector<dcp::VerificationNote> notes;
1789 shared_ptr<dcp::CPL> cpl;
1790 boost::filesystem::path dir;
1791 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1793 std::vector<dcp::VerificationNote> expected = {
1794 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1795 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
1796 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1797 dcp::VerificationNote(
1798 dcp::VerificationNote::Type::OK,
1799 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1800 dcp::String::compose("%1x%2", width, height),
1802 ).set_cpl_id(cpl->id()),
1803 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1804 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl)
1806 check_verify_result(notes, expected);
1812 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1814 vector<dcp::VerificationNote> notes;
1815 shared_ptr<dcp::CPL> cpl;
1816 boost::filesystem::path dir;
1817 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1819 std::vector<dcp::VerificationNote> expected = {
1820 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1821 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
1822 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1823 dcp::VerificationNote(
1824 dcp::VerificationNote::Type::OK,
1825 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1826 dcp::String::compose("%1x%2", width, height),
1828 ).set_cpl_id(cpl->id()),
1829 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1830 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1831 dcp::VerificationNote(
1832 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS, dcp::String::compose("%1x%2", width, height), canonical(dir / "video.mxf")
1833 ).set_cpl_id(cpl->id())
1835 check_verify_result(notes, expected);
1841 check_picture_size_bad_2k_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::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
1851 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1852 dcp::VerificationNote(
1853 dcp::VerificationNote::Type::OK,
1854 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1855 dcp::String::compose("%1x%2", width, height),
1857 ).set_cpl_id(cpl->id()),
1858 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1859 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1860 dcp::VerificationNote(
1861 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE, dcp::String::compose("%1/1", frame_rate * (three_d ? 2 : 1))
1862 ).set_cpl_id(cpl->id()),
1863 dcp::VerificationNote(
1864 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")
1865 ).set_cpl_id(cpl->id())
1868 check_verify_result(notes, expected);
1874 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1876 vector<dcp::VerificationNote> notes;
1877 shared_ptr<dcp::CPL> cpl;
1878 boost::filesystem::path dir;
1879 std::tie(notes, cpl, dir) = check_picture_size(width, height, frame_rate, three_d);
1881 std::vector<dcp::VerificationNote> expected = {
1882 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1883 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
1884 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1885 dcp::VerificationNote(
1886 dcp::VerificationNote::Type::OK,
1887 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1888 dcp::String::compose("%1x%2", width, height),
1890 ).set_cpl_id(cpl->id()),
1891 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1892 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1893 dcp::VerificationNote(
1894 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")
1895 ).set_cpl_id(cpl->id())
1898 check_verify_result(notes, expected);
1902 BOOST_AUTO_TEST_CASE (verify_picture_size)
1904 using namespace boost::filesystem;
1907 check_picture_size_ok (2048, 858, 24, false);
1908 check_picture_size_ok (2048, 858, 25, false);
1909 check_picture_size_ok (2048, 858, 48, false);
1910 check_picture_size_ok (2048, 858, 24, true);
1911 check_picture_size_ok (2048, 858, 25, true);
1912 check_picture_size_ok (2048, 858, 48, true);
1915 check_picture_size_ok (1998, 1080, 24, false);
1916 check_picture_size_ok (1998, 1080, 25, false);
1917 check_picture_size_ok (1998, 1080, 48, false);
1918 check_picture_size_ok (1998, 1080, 24, true);
1919 check_picture_size_ok (1998, 1080, 25, true);
1920 check_picture_size_ok (1998, 1080, 48, true);
1923 check_picture_size_ok (4096, 1716, 24, false);
1926 check_picture_size_ok (3996, 2160, 24, false);
1928 /* Bad frame size */
1929 check_picture_size_bad_frame_size (2050, 858, 24, false);
1930 check_picture_size_bad_frame_size (2048, 658, 25, false);
1931 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1932 check_picture_size_bad_frame_size (4000, 2000, 24, true);
1934 /* Bad 2K frame rate */
1935 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1936 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1937 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1939 /* Bad 4K frame rate */
1940 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1941 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1944 vector<dcp::VerificationNote> notes;
1945 shared_ptr<dcp::CPL> cpl;
1946 boost::filesystem::path dir;
1947 std::tie(notes, cpl, dir) = check_picture_size(3996, 2160, 24, true);
1949 std::vector<dcp::VerificationNote> expected = {
1950 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
1951 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
1952 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
1953 dcp::VerificationNote(
1954 dcp::VerificationNote::Type::OK,
1955 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
1956 string{"3996x2160"},
1958 ).set_cpl_id(cpl->id()),
1959 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
1960 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
1961 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D },
1968 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")
1971 std::make_shared<dcp::SubtitleString>(
1979 dcp::Time(start_frame, 24, 24),
1980 dcp::Time(end_frame, 24, 24),
1982 dcp::HAlign::CENTER,
1986 dcp::Direction::LTR,
1993 std::vector<dcp::Ruby>()
1999 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes)
2001 path const dir("build/test/verify_invalid_closed_caption_xml_size_in_bytes");
2002 prepare_directory (dir);
2004 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2005 for (int i = 0; i < 2048; ++i) {
2006 add_test_subtitle (asset, i * 24, i * 24 + 20);
2009 asset->set_language (dcp::LanguageTag("de-DE"));
2010 asset->write (dir / "subs.mxf");
2011 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 49148, 0);
2012 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
2014 check_verify_result (
2018 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2019 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2020 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2021 dcp::VerificationNote(
2022 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2023 ).set_cpl_id(cpl->id()),
2024 dcp::VerificationNote(
2025 dcp::VerificationNote::Type::BV21_ERROR,
2026 dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES,
2028 canonical(dir / "subs.mxf")
2029 ).set_cpl_id(cpl->id()),
2030 dcp::VerificationNote(
2031 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2032 ).set_cpl_id(cpl->id()),
2033 dcp::VerificationNote(
2034 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2035 ).set_cpl_id(cpl->id())
2041 shared_ptr<dcp::SMPTESubtitleAsset>
2042 make_large_subtitle_asset (path font_file)
2044 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2045 dcp::ArrayData big_fake_font(1024 * 1024);
2046 big_fake_font.write (font_file);
2047 for (int i = 0; i < 116; ++i) {
2048 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
2056 verify_timed_text_asset_too_large (string name)
2058 auto const dir = path("build/test") / name;
2059 prepare_directory (dir);
2060 auto asset = make_large_subtitle_asset (dir / "font.ttf");
2061 add_test_subtitle (asset, 0, 240);
2062 asset->set_language (dcp::LanguageTag("de-DE"));
2063 asset->write (dir / "subs.mxf");
2065 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 240, 0);
2066 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
2068 check_verify_result (
2072 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2073 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2074 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2075 dcp::VerificationNote(
2076 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121698284"), canonical(dir / "subs.mxf")
2077 ).set_cpl_id(cpl->id()),
2078 dcp::VerificationNote(
2079 dcp::VerificationNote::Type::BV21_ERROR,
2080 dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES,
2081 dcp::raw_convert<string>(121634816),
2082 canonical(dir / "subs.mxf")
2083 ).set_cpl_id(cpl->id()),
2084 dcp::VerificationNote(
2085 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2086 ).set_cpl_id(cpl->id()),
2087 dcp::VerificationNote(
2088 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2089 ).set_cpl_id(cpl->id()),
2090 dcp::VerificationNote(
2091 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2092 ).set_cpl_id(cpl->id())
2097 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
2099 verify_timed_text_asset_too_large<dcp::ReelSMPTESubtitleAsset>("verify_subtitle_asset_too_large");
2100 verify_timed_text_asset_too_large<dcp::ReelSMPTEClosedCaptionAsset>("verify_closed_caption_asset_too_large");
2104 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language)
2106 path dir = "build/test/verify_missing_subtitle_language";
2107 prepare_directory (dir);
2108 auto dcp = make_simple (dir, 1, 106);
2111 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
2112 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
2113 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
2114 "<ContentTitleText>Content</ContentTitleText>"
2115 "<AnnotationText>Annotation</AnnotationText>"
2116 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
2117 "<ReelNumber>1</ReelNumber>"
2118 "<EditRate>24 1</EditRate>"
2119 "<TimeCodeRate>24</TimeCodeRate>"
2120 "<StartTime>00:00:00:00</StartTime>"
2121 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
2123 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
2124 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
2125 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
2131 dcp::File xml_file(dir / "subs.xml", "w");
2132 BOOST_REQUIRE (xml_file);
2133 xml_file.write(xml.c_str(), xml.size(), 1);
2135 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
2136 subs->write (dir / "subs.mxf");
2138 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
2139 auto cpl = dcp->cpls()[0];
2140 cpl->reels()[0]->add(reel_subs);
2143 check_verify_result (
2147 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2148 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2149 dcp::VerificationNote(
2150 dcp::VerificationNote::Type::OK,
2151 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2152 string{"1998x1080"},
2154 ).set_cpl_id(cpl->id()),
2155 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
2156 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
2157 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
2158 dcp::VerificationNote(
2159 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf")
2160 ).set_cpl_id(cpl->id()),
2161 dcp::VerificationNote(
2162 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2163 ).set_cpl_id(cpl->id())
2168 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages)
2170 path path ("build/test/verify_mismatched_subtitle_languages");
2171 auto constexpr reel_length = 192;
2172 auto dcp = make_simple (path, 2, reel_length);
2173 auto cpl = dcp->cpls()[0];
2176 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2177 subs->set_language (dcp::LanguageTag("de-DE"));
2178 subs->add (simple_subtitle());
2180 subs->write (path / "subs1.mxf");
2181 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2182 cpl->reels()[0]->add(reel_subs);
2186 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2187 subs->set_language (dcp::LanguageTag("en-US"));
2188 subs->add (simple_subtitle());
2190 subs->write (path / "subs2.mxf");
2191 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2192 cpl->reels()[1]->add(reel_subs);
2197 check_verify_result (
2201 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video0.mxf"), cpl),
2202 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
2203 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2204 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2205 dcp::VerificationNote(
2206 dcp::VerificationNote::Type::OK,
2207 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2208 string{"1998x1080"},
2210 ).set_cpl_id(cpl->id()),
2211 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
2212 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video0.mxf"), cpl),
2213 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
2214 dcp::VerificationNote(
2215 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf")
2216 ).set_cpl_id(cpl->id()),
2217 dcp::VerificationNote(
2218 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf")
2219 ).set_cpl_id(cpl->id()),
2220 dcp::VerificationNote(
2221 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES
2222 ).set_cpl_id(cpl->id()),
2227 BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed)
2229 path path ("build/test/verify_multiple_closed_caption_languages_allowed");
2230 auto constexpr reel_length = 192;
2231 auto dcp = make_simple (path, 2, reel_length);
2232 auto cpl = dcp->cpls()[0];
2235 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
2236 ccaps->set_language (dcp::LanguageTag("de-DE"));
2237 ccaps->add (simple_subtitle());
2239 ccaps->write (path / "subs1.mxf");
2240 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
2241 cpl->reels()[0]->add(reel_ccaps);
2245 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
2246 ccaps->set_language (dcp::LanguageTag("en-US"));
2247 ccaps->add (simple_subtitle());
2249 ccaps->write (path / "subs2.mxf");
2250 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
2251 cpl->reels()[1]->add(reel_ccaps);
2256 check_verify_result (
2260 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2261 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2262 dcp::VerificationNote(
2263 dcp::VerificationNote::Type::OK,
2264 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2265 string{"1998x1080"},
2267 ).set_cpl_id(cpl->id()),
2268 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
2269 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video0.mxf"), cpl),
2270 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
2271 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video0.mxf"), cpl),
2272 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
2273 dcp::VerificationNote(
2274 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf")
2275 ).set_cpl_id(cpl->id()),
2276 dcp::VerificationNote(
2277 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf")
2278 ).set_cpl_id(cpl->id())
2283 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time)
2285 path dir = "build/test/verify_missing_subtitle_start_time";
2286 prepare_directory (dir);
2287 auto dcp = make_simple (dir, 1, 106);
2290 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
2291 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
2292 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
2293 "<ContentTitleText>Content</ContentTitleText>"
2294 "<AnnotationText>Annotation</AnnotationText>"
2295 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
2296 "<ReelNumber>1</ReelNumber>"
2297 "<Language>de-DE</Language>"
2298 "<EditRate>24 1</EditRate>"
2299 "<TimeCodeRate>24</TimeCodeRate>"
2300 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
2302 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
2303 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
2304 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
2310 dcp::File xml_file(dir / "subs.xml", "w");
2311 BOOST_REQUIRE (xml_file);
2312 xml_file.write(xml.c_str(), xml.size(), 1);
2314 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
2315 subs->write (dir / "subs.mxf");
2317 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
2318 auto cpl = dcp->cpls()[0];
2319 cpl->reels()[0]->add(reel_subs);
2322 check_verify_result (
2326 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
2327 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2328 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2329 dcp::VerificationNote(
2330 dcp::VerificationNote::Type::OK,
2331 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2332 string{"1998x1080"},
2334 ).set_cpl_id(cpl->id()),
2335 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
2336 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
2337 dcp::VerificationNote(
2338 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2339 ).set_cpl_id(cpl->id()),
2340 dcp::VerificationNote(
2341 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2342 ).set_cpl_id(cpl->id())
2347 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time)
2349 path dir = "build/test/verify_invalid_subtitle_start_time";
2350 prepare_directory (dir);
2351 auto dcp = make_simple (dir, 1, 106);
2354 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
2355 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
2356 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
2357 "<ContentTitleText>Content</ContentTitleText>"
2358 "<AnnotationText>Annotation</AnnotationText>"
2359 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
2360 "<ReelNumber>1</ReelNumber>"
2361 "<Language>de-DE</Language>"
2362 "<EditRate>24 1</EditRate>"
2363 "<TimeCodeRate>24</TimeCodeRate>"
2364 "<StartTime>00:00:02:00</StartTime>"
2365 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
2367 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
2368 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
2369 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
2375 dcp::File xml_file(dir / "subs.xml", "w");
2376 BOOST_REQUIRE (xml_file);
2377 xml_file.write(xml.c_str(), xml.size(), 1);
2379 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
2380 subs->write (dir / "subs.mxf");
2382 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
2383 auto cpl = dcp->cpls()[0];
2384 cpl->reels().front()->add(reel_subs);
2387 check_verify_result (
2391 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2392 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2393 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
2394 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
2395 dcp::VerificationNote(
2396 dcp::VerificationNote::Type::OK,
2397 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
2398 string{"1998x1080"},
2400 ).set_cpl_id(cpl->id()),
2401 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
2402 dcp::VerificationNote(
2403 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf")
2404 ).set_cpl_id(cpl->id()),
2405 dcp::VerificationNote(
2406 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2407 ).set_cpl_id(cpl->id())
2415 TestText (int in_, int out_, float v_position_ = 0, dcp::VAlign v_align_ = dcp::VAlign::CENTER, string text_ = "Hello")
2418 , v_position(v_position_)
2426 dcp::VAlign v_align;
2432 shared_ptr<dcp::CPL>
2433 dcp_with_text(path dir, vector<TestText> subs, optional<dcp::Key> key = boost::none, optional<string> key_id = boost::none)
2435 prepare_directory (dir);
2436 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2437 asset->set_start_time (dcp::Time());
2438 for (auto i: subs) {
2439 add_test_subtitle (asset, i.in, i.out, i.v_position, i.v_align, i.text);
2441 asset->set_language (dcp::LanguageTag("de-DE"));
2442 if (key && key_id) {
2443 asset->set_key(*key);
2444 asset->set_key_id(*key_id);
2447 asset->write (dir / "subs.mxf");
2449 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
2450 return write_dcp_with_single_asset (dir, reel_asset);
2455 shared_ptr<dcp::CPL>
2456 dcp_with_text_from_file (path dir, boost::filesystem::path subs_xml)
2458 prepare_directory (dir);
2459 auto asset = make_shared<dcp::SMPTESubtitleAsset>(subs_xml);
2460 asset->set_start_time (dcp::Time());
2461 asset->set_language (dcp::LanguageTag("de-DE"));
2463 auto subs_mxf = dir / "subs.mxf";
2464 asset->write (subs_mxf);
2466 /* The call to write() puts the asset into the DCP correctly but it will have
2467 * XML re-written by our parser. Overwrite the MXF using the given file's verbatim
2470 ASDCP::TimedText::MXFWriter writer;
2471 ASDCP::WriterInfo writer_info;
2472 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
2474 Kumu::hex2bin (asset->id().c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
2475 DCP_ASSERT (c == Kumu::UUID_Length);
2476 ASDCP::TimedText::TimedTextDescriptor descriptor;
2477 descriptor.ContainerDuration = asset->intrinsic_duration();
2478 Kumu::hex2bin (asset->xml_id()->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c);
2479 DCP_ASSERT (c == Kumu::UUID_Length);
2480 ASDCP::Result_t r = writer.OpenWrite (subs_mxf.string().c_str(), writer_info, descriptor, 16384);
2481 BOOST_REQUIRE (!ASDCP_FAILURE(r));
2482 r = writer.WriteTimedTextResource (dcp::file_to_string(subs_xml));
2483 BOOST_REQUIRE (!ASDCP_FAILURE(r));
2486 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
2487 return write_dcp_with_single_asset (dir, reel_asset);
2491 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time)
2493 auto const dir = path("build/test/verify_invalid_subtitle_first_text_time");
2494 /* Just too early */
2495 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
2496 check_verify_result (
2500 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2501 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2502 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2503 dcp::VerificationNote(
2504 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2505 ).set_cpl_id(cpl->id()),
2506 dcp::VerificationNote(
2507 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2508 ).set_cpl_id(cpl->id())
2514 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time)
2516 auto const dir = path("build/test/verify_valid_subtitle_first_text_time");
2517 /* Just late enough */
2518 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
2519 check_verify_result(
2523 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2524 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2525 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2526 dcp::VerificationNote(
2527 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2528 ).set_cpl_id(cpl->id())
2533 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel)
2535 auto const dir = path("build/test/verify_valid_subtitle_first_text_time_on_second_reel");
2536 prepare_directory (dir);
2538 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
2539 asset1->set_start_time (dcp::Time());
2540 /* Just late enough */
2541 add_test_subtitle (asset1, 4 * 24, 5 * 24);
2542 asset1->set_language (dcp::LanguageTag("de-DE"));
2544 asset1->write (dir / "subs1.mxf");
2545 auto reel_asset1 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset1, dcp::Fraction(24, 1), 5 * 24, 0);
2546 auto reel1 = make_shared<dcp::Reel>();
2547 reel1->add (reel_asset1);
2548 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 5 * 24);
2549 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2550 reel1->add (markers1);
2552 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
2553 asset2->set_start_time (dcp::Time());
2555 /* This would be too early on first reel but should be OK on the second */
2556 add_test_subtitle (asset2, 3, 4 * 24);
2557 asset2->set_language (dcp::LanguageTag("de-DE"));
2558 asset2->write (dir / "subs2.mxf");
2559 auto reel_asset2 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset2, dcp::Fraction(24, 1), 4 * 24, 0);
2560 auto reel2 = make_shared<dcp::Reel>();
2561 reel2->add (reel_asset2);
2562 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 4 * 24);
2563 markers2->set (dcp::Marker::LFOC, dcp::Time(4 * 24 - 1, 24, 24));
2564 reel2->add (markers2);
2566 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2569 auto dcp = make_shared<dcp::DCP>(dir);
2571 dcp->set_annotation_text("hello");
2574 check_verify_result(
2578 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2579 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2580 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2581 dcp::VerificationNote(
2582 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2583 ).set_cpl_id(cpl->id())
2588 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing)
2590 auto const dir = path("build/test/verify_invalid_subtitle_spacing");
2591 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2595 { 5 * 24 + 1, 6 * 24 },
2597 check_verify_result (
2601 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2602 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2603 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2604 dcp::VerificationNote(
2605 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING
2606 ).set_cpl_id(cpl->id()),
2607 dcp::VerificationNote(
2608 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2609 ).set_cpl_id(cpl->id())
2614 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_spacing)
2616 auto const dir = path("build/test/verify_valid_subtitle_spacing");
2617 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2621 { 5 * 24 + 16, 8 * 24 },
2624 check_verify_result(
2628 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2629 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2630 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2631 dcp::VerificationNote(
2632 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2633 ).set_cpl_id(cpl->id())
2638 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_duration)
2640 auto const dir = path("build/test/verify_invalid_subtitle_duration");
2641 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
2642 check_verify_result (
2646 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2647 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2648 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2649 dcp::VerificationNote(
2650 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION
2651 ).set_cpl_id(cpl->id()),
2652 dcp::VerificationNote(
2653 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2654 ).set_cpl_id(cpl->id())
2659 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_duration)
2661 auto const dir = path("build/test/verify_valid_subtitle_duration");
2662 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
2664 check_verify_result(
2668 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2669 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2670 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2671 dcp::VerificationNote(
2672 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2673 ).set_cpl_id(cpl->id())
2678 BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary)
2680 auto const dir = path("build/test/verify_subtitle_overlapping_reel_boundary");
2681 prepare_directory (dir);
2682 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
2683 asset->set_start_time (dcp::Time());
2684 add_test_subtitle (asset, 0, 4 * 24);
2686 asset->set_language (dcp::LanguageTag("de-DE"));
2687 asset->write (dir / "subs.mxf");
2689 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 3 * 24, 0);
2690 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
2691 check_verify_result (
2695 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2696 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2697 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2698 dcp::VerificationNote(
2699 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get())
2700 ).set_cpl_id(cpl->id()),
2701 dcp::VerificationNote(
2702 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
2703 ).set_cpl_id(cpl->id()),
2704 dcp::VerificationNote(
2705 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY
2706 ).set_cpl_id(cpl->id()),
2707 dcp::VerificationNote(
2708 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2709 ).set_cpl_id(cpl->id())
2715 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count1)
2717 auto const dir = path ("build/test/invalid_subtitle_line_count1");
2718 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2721 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2722 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2723 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2724 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
2726 check_verify_result (
2730 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2731 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2732 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, 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_count1)
2745 auto const dir = path ("build/test/verify_valid_subtitle_line_count1");
2746 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2749 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2750 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2751 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2754 check_verify_result(
2758 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2759 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2760 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, 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_count2)
2770 auto const dir = path ("build/test/verify_invalid_subtitle_line_count2");
2771 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2774 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2775 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2776 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2777 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2779 check_verify_result (
2783 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2784 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2785 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2786 dcp::VerificationNote(
2787 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT
2788 ).set_cpl_id(cpl->id()),
2789 dcp::VerificationNote(
2790 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2791 ).set_cpl_id(cpl->id())
2796 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count2)
2798 auto const dir = path ("build/test/verify_valid_subtitle_line_count2");
2799 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2802 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2803 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2804 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2805 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2808 check_verify_result(
2812 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2813 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2814 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2815 dcp::VerificationNote(
2816 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2817 ).set_cpl_id(cpl->id())
2822 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length1)
2824 auto const dir = path ("build/test/verify_invalid_subtitle_line_length1");
2825 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2828 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123" }
2830 check_verify_result (
2834 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2835 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2836 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2837 dcp::VerificationNote(
2838 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH
2839 ).set_cpl_id(cpl->id()),
2840 dcp::VerificationNote(
2841 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2842 ).set_cpl_id(cpl->id())
2847 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length2)
2849 auto const dir = path ("build/test/verify_invalid_subtitle_line_length2");
2850 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
2853 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
2855 check_verify_result (
2859 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2860 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2861 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2862 dcp::VerificationNote(
2863 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH
2864 ).set_cpl_id(cpl->id()),
2865 dcp::VerificationNote(
2866 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2867 ).set_cpl_id(cpl->id())
2872 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count1)
2874 auto const dir = path ("build/test/verify_valid_closed_caption_line_count1");
2875 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2878 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2879 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2880 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2881 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
2883 check_verify_result (
2887 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2888 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2889 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2890 dcp::VerificationNote(
2891 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT
2892 ).set_cpl_id(cpl->id()),
2893 dcp::VerificationNote(
2894 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2895 ).set_cpl_id(cpl->id())
2900 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count2)
2902 auto const dir = path ("build/test/verify_valid_closed_caption_line_count2");
2903 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2906 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2907 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2908 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2911 check_verify_result(
2915 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2916 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2917 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2918 dcp::VerificationNote(
2919 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2920 ).set_cpl_id(cpl->id())
2925 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_count3)
2927 auto const dir = path ("build/test/verify_invalid_closed_caption_line_count3");
2928 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2931 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2932 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2933 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2934 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2936 check_verify_result (
2940 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2941 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2942 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2943 dcp::VerificationNote(
2944 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT
2945 ).set_cpl_id(cpl->id()),
2946 dcp::VerificationNote(
2947 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2948 ).set_cpl_id(cpl->id())
2953 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count4)
2955 auto const dir = path ("build/test/verify_valid_closed_caption_line_count4");
2956 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2959 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2960 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2961 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2962 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2965 check_verify_result(
2969 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2970 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2971 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2972 dcp::VerificationNote(
2973 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2974 ).set_cpl_id(cpl->id())
2979 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_length)
2981 auto const dir = path ("build/test/verify_valid_closed_caption_line_length");
2982 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2985 { 96, 300, 0.0, dcp::VAlign::CENTER, "01234567890123456789012345678901" }
2988 check_verify_result (
2992 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
2993 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
2994 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
2995 dcp::VerificationNote(
2996 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
2997 ).set_cpl_id(cpl->id())
3002 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length)
3004 auto const dir = path ("build/test/verify_invalid_closed_caption_line_length");
3005 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3008 { 96, 300, 0.0, dcp::VAlign::CENTER, "0123456789012345678901234567890123" }
3010 check_verify_result (
3014 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3015 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3016 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3017 dcp::VerificationNote(
3018 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH
3019 ).set_cpl_id(cpl->id()),
3020 dcp::VerificationNote(
3021 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3022 ).set_cpl_id(cpl->id())
3027 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1)
3029 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign1");
3030 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3033 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
3034 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
3035 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
3037 check_verify_result (
3041 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3042 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3043 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3044 dcp::VerificationNote(
3045 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3046 ).set_cpl_id(cpl->id())
3051 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2)
3053 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign2");
3054 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3057 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
3058 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
3059 { 96, 300, 0.2, dcp::VAlign::CENTER, "not fine" },
3061 check_verify_result (
3065 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3066 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3067 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3068 dcp::VerificationNote(
3069 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN
3070 ).set_cpl_id(cpl->id()),
3071 dcp::VerificationNote(
3072 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3073 ).set_cpl_id(cpl->id())
3078 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1)
3080 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering1");
3081 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3084 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
3085 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
3086 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
3089 check_verify_result(
3093 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3094 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3095 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3096 dcp::VerificationNote(
3097 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3098 ).set_cpl_id(cpl->id())
3103 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2)
3105 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering2");
3106 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
3109 { 96, 300, 0.2, dcp::VAlign::BOTTOM, "This" },
3110 { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" },
3111 { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" },
3114 check_verify_result(
3118 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3119 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3120 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3121 dcp::VerificationNote(
3122 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3123 ).set_cpl_id(cpl->id())
3128 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3)
3130 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering3");
3131 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering3.xml");
3132 check_verify_result (
3136 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3137 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3138 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3139 dcp::VerificationNote(
3140 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING
3141 ).set_cpl_id(cpl->id()),
3142 dcp::VerificationNote(
3143 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3144 ).set_cpl_id(cpl->id())
3149 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4)
3151 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4");
3152 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml");
3154 check_verify_result(
3158 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3159 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3160 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3161 dcp::VerificationNote(
3162 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3163 ).set_cpl_id(cpl->id())
3169 BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate)
3171 path const dir("build/test/verify_invalid_sound_frame_rate");
3172 prepare_directory (dir);
3174 auto picture = simple_picture (dir, "foo");
3175 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
3176 auto reel = make_shared<dcp::Reel>();
3177 reel->add (reel_picture);
3178 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000, boost::none);
3179 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
3180 reel->add (reel_sound);
3181 reel->add (simple_markers());
3182 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3184 auto dcp = make_shared<dcp::DCP>(dir);
3186 dcp->set_annotation_text("hello");
3189 check_verify_result (
3193 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3194 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
3195 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "videofoo.mxf"), cpl),
3196 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3197 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "videofoo.mxf"), cpl),
3198 dcp::VerificationNote(
3199 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf")
3200 ).set_cpl_id(cpl->id()),
3201 dcp::VerificationNote(
3202 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3203 ).set_cpl_id(cpl->id())
3208 BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text)
3210 path const dir("build/test/verify_missing_cpl_annotation_text");
3211 auto dcp = make_simple (dir);
3214 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3216 auto const cpl = dcp->cpls()[0];
3218 HashCalculator calc(cpl->file().get());
3221 BOOST_REQUIRE (cpl->file());
3222 Editor e(cpl->file().get());
3223 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
3226 check_verify_result (
3230 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3231 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3232 dcp::VerificationNote(
3233 dcp::VerificationNote::Type::OK,
3234 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3235 string{"1998x1080"},
3237 ).set_cpl_id(cpl->id()),
3238 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3239 dcp::VerificationNote(
3240 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, canonical(cpl->file().get())
3241 ).set_cpl_id(cpl->id()),
3242 dcp::VerificationNote(
3243 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get())
3244 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash())
3249 BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text)
3251 path const dir("build/test/verify_mismatched_cpl_annotation_text");
3252 auto dcp = make_simple (dir);
3255 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3256 auto const cpl = dcp->cpls()[0];
3258 HashCalculator calc(cpl->file().get());
3261 BOOST_REQUIRE (cpl->file());
3262 Editor e(cpl->file().get());
3263 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
3266 check_verify_result (
3270 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3271 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3272 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3273 dcp::VerificationNote(
3274 dcp::VerificationNote::Type::OK,
3275 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3276 string{"1998x1080"},
3278 ).set_cpl_id(cpl->id()),
3279 dcp::VerificationNote(
3280 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, canonical(cpl->file().get())
3281 ).set_cpl_id(cpl->id()),
3282 dcp::VerificationNote(
3283 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl->file().get())
3284 ).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()).set_cpl_id(cpl->id())
3289 BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration)
3291 path const dir("build/test/verify_mismatched_asset_duration");
3292 prepare_directory (dir);
3293 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
3294 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3296 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
3297 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
3299 auto reel = make_shared<dcp::Reel>(
3300 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
3301 make_shared<dcp::ReelSoundAsset>(ms, 0)
3304 reel->add (simple_markers());
3308 dcp->set_annotation_text("A Test DCP");
3311 check_verify_result (
3315 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3316 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3317 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3318 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3319 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3320 dcp::VerificationNote(
3321 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION
3322 ).set_cpl_id(cpl->id()),
3323 dcp::VerificationNote(
3324 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl->file().get())
3325 ).set_cpl_id(cpl->id())
3332 shared_ptr<dcp::CPL>
3333 verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool add_to_reel2)
3335 prepare_directory (dir);
3336 auto dcp = make_shared<dcp::DCP>(dir);
3337 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3339 auto constexpr reel_length = 192;
3341 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
3342 subs->set_language (dcp::LanguageTag("de-DE"));
3343 subs->set_start_time (dcp::Time());
3344 subs->add (simple_subtitle());
3346 subs->write (dir / "subs.mxf");
3347 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
3349 auto reel1 = make_shared<dcp::Reel>(
3350 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "1", reel_length), 0),
3351 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "1", dcp::MXFMetadata(), "en-US", reel_length), 0)
3355 reel1->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3358 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3359 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
3360 reel1->add (markers1);
3364 auto reel2 = make_shared<dcp::Reel>(
3365 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "2", reel_length), 0),
3366 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "2", dcp::MXFMetadata(), "en-US", reel_length), 0)
3370 reel2->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3373 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3374 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
3375 reel2->add (markers2);
3380 dcp->set_annotation_text("A Test DCP");
3387 BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels)
3390 path dir ("build/test/missing_main_subtitle_from_some_reels");
3391 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, false);
3392 check_verify_result (
3396 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3397 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3398 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3399 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3400 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3401 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3402 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3403 dcp::VerificationNote(
3404 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS
3405 ).set_cpl_id(cpl->id()),
3406 dcp::VerificationNote(
3407 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3408 ).set_cpl_id(cpl->id())
3414 path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
3415 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, true);
3416 check_verify_result(
3420 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3421 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3422 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3423 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3424 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3425 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3426 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3427 dcp::VerificationNote(
3428 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3429 ).set_cpl_id(cpl->id())
3434 path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
3435 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, false, false);
3436 check_verify_result(
3440 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3441 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3442 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3443 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3444 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3445 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3446 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3447 dcp::VerificationNote(
3448 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3449 ).set_cpl_id(cpl->id())
3456 shared_ptr<dcp::CPL>
3457 verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, int caps_in_reel2)
3459 prepare_directory (dir);
3460 auto dcp = make_shared<dcp::DCP>(dir);
3461 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3463 auto constexpr reel_length = 192;
3465 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
3466 subs->set_language (dcp::LanguageTag("de-DE"));
3467 subs->set_start_time (dcp::Time());
3468 subs->add (simple_subtitle());
3470 subs->write (dir / "subs.mxf");
3472 auto reel1 = make_shared<dcp::Reel>(
3473 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "1", reel_length), 0),
3474 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "1", dcp::MXFMetadata(), "en-US", reel_length), 0)
3477 for (int i = 0; i < caps_in_reel1; ++i) {
3478 reel1->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3481 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3482 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
3483 reel1->add (markers1);
3487 auto reel2 = make_shared<dcp::Reel>(
3488 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "2", reel_length), 0),
3489 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "2", dcp::MXFMetadata(), "en-US", reel_length), 0)
3492 for (int i = 0; i < caps_in_reel2; ++i) {
3493 reel2->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
3496 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
3497 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
3498 reel2->add (markers2);
3503 dcp->set_annotation_text("A Test DCP");
3510 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_asset_counts)
3513 path dir ("build/test/mismatched_closed_caption_asset_counts");
3514 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
3515 check_verify_result (
3519 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3520 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3521 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3522 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3523 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3524 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3525 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3526 dcp::VerificationNote(
3527 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS
3528 ).set_cpl_id(cpl->id()),
3529 dcp::VerificationNote(
3530 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3531 ).set_cpl_id(cpl->id())
3536 path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
3537 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
3538 check_verify_result(
3542 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3543 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3544 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3545 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3546 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3547 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3548 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3549 dcp::VerificationNote(
3550 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3551 ).set_cpl_id(cpl->id())
3556 path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
3557 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
3558 check_verify_result(
3562 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3563 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video1.mxf"), cpl),
3564 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video2.mxf"), cpl),
3565 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3566 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3567 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video1.mxf"), cpl),
3568 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video2.mxf"), cpl),
3569 dcp::VerificationNote(
3570 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3571 ).set_cpl_id(cpl->id())
3579 verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
3581 prepare_directory (dir);
3582 auto dcp = make_shared<dcp::DCP>(dir);
3583 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3585 auto constexpr reel_length = 192;
3587 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
3588 subs->set_language (dcp::LanguageTag("de-DE"));
3589 subs->set_start_time (dcp::Time());
3590 subs->add (simple_subtitle());
3592 subs->write (dir / "subs.mxf");
3593 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), reel_length, 0);
3596 auto reel = make_shared<dcp::Reel>(
3597 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
3598 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
3601 reel->add (reel_text);
3603 reel->add (simple_markers(reel_length));
3608 dcp->set_annotation_text("A Test DCP");
3611 check_verify_result (
3615 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3616 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3617 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3618 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3619 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3620 dcp::VerificationNote(
3621 dcp::VerificationNote::Type::BV21_ERROR, code, subs->id()
3622 ).set_cpl_id(cpl->id()),
3623 dcp::VerificationNote(
3624 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
3625 ).set_cpl_id(cpl->id())
3630 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
3632 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
3633 "build/test/verify_subtitle_entry_point_must_be_present",
3634 dcp::VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT,
3635 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
3636 asset->unset_entry_point ();
3640 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
3641 "build/test/verify_subtitle_entry_point_must_be_zero",
3642 dcp::VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT,
3643 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
3644 asset->set_entry_point (4);
3648 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
3649 "build/test/verify_closed_caption_entry_point_must_be_present",
3650 dcp::VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT,
3651 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
3652 asset->unset_entry_point ();
3656 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
3657 "build/test/verify_closed_caption_entry_point_must_be_zero",
3658 dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT,
3659 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
3660 asset->set_entry_point (9);
3666 BOOST_AUTO_TEST_CASE (verify_missing_hash)
3670 path const dir("build/test/verify_missing_hash");
3671 auto dcp = make_simple (dir);
3674 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3675 auto const cpl = dcp->cpls()[0];
3676 BOOST_REQUIRE_EQUAL (cpl->reels().size(), 1U);
3677 BOOST_REQUIRE (cpl->reels()[0]->main_picture());
3678 auto asset_id = cpl->reels()[0]->main_picture()->id();
3680 HashCalculator calc(cpl->file().get());
3683 BOOST_REQUIRE (cpl->file());
3684 Editor e(cpl->file().get());
3685 e.delete_first_line_containing("<Hash>");
3688 check_verify_result (
3692 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3693 dcp::VerificationNote(
3694 dcp::VerificationNote::Type::OK,
3695 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3696 string{"1998x1080"},
3698 ).set_cpl_id(cpl->id()),
3699 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3700 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3701 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3702 dcp::VerificationNote(
3703 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3704 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3705 dcp::VerificationNote(
3706 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id
3707 ).set_cpl_id(cpl->id())
3714 verify_markers_test (
3716 vector<pair<dcp::Marker, dcp::Time>> markers,
3717 vector<dcp::VerificationNote> test_notes
3720 auto dcp = make_simple (dir);
3721 auto cpl = dcp->cpls()[0];
3722 cpl->set_content_kind(dcp::ContentKind::FEATURE);
3723 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24);
3724 for (auto const& i: markers) {
3725 markers_asset->set (i.first, i.second);
3727 cpl->reels()[0]->add(markers_asset);
3730 for (auto& note: test_notes) {
3731 note.set_cpl_id(cpl->id());
3734 test_notes.push_back(ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl));
3735 test_notes.push_back(ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl));
3736 test_notes.push_back(ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl));
3737 test_notes.push_back(
3738 dcp::VerificationNote(
3739 dcp::VerificationNote::Type::OK,
3740 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3741 string{"1998x1080"},
3743 ).set_cpl_id(cpl->id())
3745 test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl));
3746 test_notes.push_back(ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl));
3747 check_verify_result({dir}, {}, test_notes);
3751 BOOST_AUTO_TEST_CASE (verify_markers)
3753 verify_markers_test (
3754 "build/test/verify_markers_all_correct",
3756 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3757 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3758 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3759 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3764 verify_markers_test (
3765 "build/test/verify_markers_missing_ffec",
3767 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3768 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3769 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3772 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }
3775 verify_markers_test (
3776 "build/test/verify_markers_missing_ffmc",
3778 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3779 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3780 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3783 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }
3786 verify_markers_test (
3787 "build/test/verify_markers_missing_ffoc",
3789 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3790 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3791 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3794 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC}
3797 verify_markers_test (
3798 "build/test/verify_markers_missing_lfoc",
3800 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3801 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3802 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
3805 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }
3808 verify_markers_test (
3809 "build/test/verify_markers_incorrect_ffoc",
3811 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3812 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3813 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
3814 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
3817 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_FFOC, string("3") }
3820 verify_markers_test (
3821 "build/test/verify_markers_incorrect_lfoc",
3823 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
3824 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
3825 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
3826 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
3829 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_LFOC, string("18") }
3834 BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number)
3836 path dir = "build/test/verify_missing_cpl_metadata_version_number";
3837 prepare_directory (dir);
3838 auto dcp = make_simple (dir);
3839 auto cpl = dcp->cpls()[0];
3840 cpl->unset_version_number();
3843 check_verify_result(
3847 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3848 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
3849 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3850 dcp::VerificationNote(
3851 dcp::VerificationNote::Type::OK,
3852 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3853 string{"1998x1080"},
3855 ).set_cpl_id(cpl->id()),
3856 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3857 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3858 dcp::VerificationNote(
3859 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->file().get()
3860 ).set_cpl_id(cpl->id())
3865 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1)
3867 path dir = "build/test/verify_missing_extension_metadata1";
3868 auto dcp = make_simple (dir);
3871 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
3872 auto cpl = dcp->cpls()[0];
3874 HashCalculator calc(cpl->file().get());
3877 Editor e (cpl->file().get());
3878 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
3881 check_verify_result (
3885 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3886 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3887 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3888 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3889 dcp::VerificationNote(
3890 dcp::VerificationNote::Type::OK,
3891 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3892 string{"1998x1080"},
3894 ).set_cpl_id(cpl->id()),
3895 dcp::VerificationNote(
3896 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3897 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3898 dcp::VerificationNote(
3899 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get()
3900 ).set_cpl_id(cpl->id())
3905 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2)
3907 path dir = "build/test/verify_missing_extension_metadata2";
3908 auto dcp = make_simple (dir);
3911 auto cpl = dcp->cpls()[0];
3913 HashCalculator calc(cpl->file().get());
3916 Editor e (cpl->file().get());
3917 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
3920 check_verify_result (
3924 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3925 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3926 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3927 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3928 dcp::VerificationNote(
3929 dcp::VerificationNote::Type::OK,
3930 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3931 string{"1998x1080"},
3933 ).set_cpl_id(cpl->id()),
3934 dcp::VerificationNote(
3935 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3936 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3937 dcp::VerificationNote(
3938 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->file().get()
3939 ).set_cpl_id(cpl->id())
3944 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3)
3946 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata3";
3947 auto dcp = make_simple (dir);
3950 auto const cpl = dcp->cpls()[0];
3952 HashCalculator calc(cpl->file().get());
3955 Editor e (cpl->file().get());
3956 e.replace ("<meta:Name>A", "<meta:NameX>A");
3957 e.replace ("n</meta:Name>", "n</meta:NameX>");
3960 check_verify_result (
3964 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
3965 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
3966 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
3967 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
3968 dcp::VerificationNote(
3969 dcp::VerificationNote::Type::OK,
3970 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
3971 string{"1998x1080"},
3973 ).set_cpl_id(cpl->id()),
3974 dcp::VerificationNote(
3975 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70
3976 ).set_cpl_id(cpl->id()),
3977 dcp::VerificationNote(
3978 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()),
3979 dcp::VerificationNote(
3980 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
3981 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
3986 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1)
3988 path dir = "build/test/verify_invalid_extension_metadata1";
3989 auto dcp = make_simple (dir);
3992 auto cpl = dcp->cpls()[0];
3994 HashCalculator calc(cpl->file().get());
3997 Editor e (cpl->file().get());
3998 e.replace ("Application", "Fred");
4001 check_verify_result (
4005 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4006 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4007 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4008 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4009 dcp::VerificationNote(
4010 dcp::VerificationNote::Type::OK,
4011 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4012 string{"1998x1080"},
4014 ).set_cpl_id(cpl->id()),
4015 dcp::VerificationNote(
4016 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4017 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4018 dcp::VerificationNote(
4019 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get()
4020 ).set_cpl_id(cpl->id())
4025 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2)
4027 path dir = "build/test/verify_invalid_extension_metadata2";
4028 auto dcp = make_simple (dir);
4031 auto cpl = dcp->cpls()[0];
4033 HashCalculator calc(cpl->file().get());
4036 Editor e (cpl->file().get());
4037 e.replace ("DCP Constraints Profile", "Fred");
4040 check_verify_result (
4044 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4045 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4046 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4047 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4048 dcp::VerificationNote(
4049 dcp::VerificationNote::Type::OK,
4050 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4051 string{"1998x1080"},
4053 ).set_cpl_id(cpl->id()),
4054 dcp::VerificationNote(
4055 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4056 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4057 dcp::VerificationNote(
4058 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get()
4059 ).set_cpl_id(cpl->id())
4064 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6)
4066 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata6";
4067 auto dcp = make_simple (dir);
4070 auto const cpl = dcp->cpls()[0];
4072 HashCalculator calc(cpl->file().get());
4075 Editor e (cpl->file().get());
4076 e.replace ("<meta:Value>", "<meta:ValueX>");
4077 e.replace ("</meta:Value>", "</meta:ValueX>");
4080 check_verify_result (
4084 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4085 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4086 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4087 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4088 dcp::VerificationNote(
4089 dcp::VerificationNote::Type::OK,
4090 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4091 string{"1998x1080"},
4093 ).set_cpl_id(cpl->id()),
4094 dcp::VerificationNote(
4095 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74
4096 ).set_cpl_id(cpl->id()),
4097 dcp::VerificationNote(
4098 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
4099 ).set_cpl_id(cpl->id()),
4100 dcp::VerificationNote(
4101 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4102 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash())
4107 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7)
4109 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata7";
4110 auto dcp = make_simple (dir);
4113 auto const cpl = dcp->cpls()[0];
4115 HashCalculator calc(cpl->file().get());
4118 Editor e (cpl->file().get());
4119 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
4122 check_verify_result (
4126 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4127 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4128 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4129 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4130 dcp::VerificationNote(
4131 dcp::VerificationNote::Type::OK,
4132 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4133 string{"1998x1080"},
4135 ).set_cpl_id(cpl->id()),
4136 dcp::VerificationNote(
4137 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4138 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4139 dcp::VerificationNote(
4140 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()
4141 ).set_cpl_id(cpl->id())
4146 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8)
4148 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata8";
4149 auto dcp = make_simple (dir);
4152 auto const cpl = dcp->cpls()[0];
4154 HashCalculator calc(cpl->file().get());
4157 Editor e (cpl->file().get());
4158 e.replace ("<meta:Property>", "<meta:PropertyX>");
4159 e.replace ("</meta:Property>", "</meta:PropertyX>");
4162 check_verify_result (
4166 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4167 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4168 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4169 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4170 dcp::VerificationNote(
4171 dcp::VerificationNote::Type::OK,
4172 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4173 string{"1998x1080"},
4175 ).set_cpl_id(cpl->id()),
4176 dcp::VerificationNote(
4177 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72
4178 ).set_cpl_id(cpl->id()),
4179 dcp::VerificationNote(
4180 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()),
4181 dcp::VerificationNote(
4182 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4183 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4188 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9)
4190 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata9";
4191 auto dcp = make_simple (dir);
4194 auto const cpl = dcp->cpls()[0];
4196 HashCalculator calc(cpl->file().get());
4199 Editor e (cpl->file().get());
4200 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
4201 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
4204 check_verify_result (
4208 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4209 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4210 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4211 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4212 dcp::VerificationNote(
4213 dcp::VerificationNote::Type::OK,
4214 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4215 string{"1998x1080"},
4217 ).set_cpl_id(cpl->id()),
4218 dcp::VerificationNote(
4219 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71
4220 ).set_cpl_id(cpl->id()),
4221 dcp::VerificationNote(
4222 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
4223 ).set_cpl_id(cpl->id()),
4224 dcp::VerificationNote(
4225 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->file().get()
4226 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4232 BOOST_AUTO_TEST_CASE (verify_unsigned_cpl_with_encrypted_content)
4234 path const dir = "build/test/verify_unsigned_cpl_with_encrypted_content";
4235 prepare_directory (dir);
4236 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
4237 copy_file (i.path(), dir / i.path().filename());
4240 path const pkl = dir / ( "pkl_" + encryption_test_pkl_id() + ".xml");
4241 path const cpl_path = dir / ( "cpl_" + encryption_test_cpl_id() + ".xml");
4243 HashCalculator calc(cpl_path);
4247 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
4250 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
4252 check_verify_result (
4256 ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl),
4257 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4258 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4259 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4260 dcp::VerificationNote(
4261 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(cpl_path)
4262 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4263 dcp::VerificationNote(
4264 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl)
4265 ).set_cpl_id(cpl->id()),
4266 dcp::VerificationNote(
4267 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE
4268 ).set_cpl_id(cpl->id()),
4269 dcp::VerificationNote(
4270 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE
4271 ).set_cpl_id(cpl->id()),
4272 dcp::VerificationNote(
4273 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC
4274 ).set_cpl_id(cpl->id()),
4275 dcp::VerificationNote(
4276 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC
4277 ).set_cpl_id(cpl->id()),
4278 dcp::VerificationNote(
4279 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_path)
4280 ).set_cpl_id(cpl->id()),
4281 dcp::VerificationNote(
4282 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, canonical(cpl_path)
4283 ).set_cpl_id(cpl->id())
4288 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_encrypted_content)
4290 path dir = "build/test/unsigned_pkl_with_encrypted_content";
4291 prepare_directory (dir);
4292 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
4293 copy_file (i.path(), dir / i.path().filename());
4296 path const cpl_path = dir / ("cpl_" + encryption_test_cpl_id() + ".xml");
4297 path const pkl = dir / ("pkl_" + encryption_test_pkl_id() + ".xml");
4300 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
4303 auto cpl = std::make_shared<dcp::CPL>(cpl_path);
4305 check_verify_result (
4309 ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl),
4310 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4311 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4312 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4313 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4314 dcp::VerificationNote(
4315 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id(), canonical(pkl)
4316 ).set_cpl_id(cpl->id()),
4317 dcp::VerificationNote(
4318 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE
4319 ).set_cpl_id(cpl->id()),
4320 dcp::VerificationNote(
4321 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE
4322 ).set_cpl_id(cpl->id()),
4323 dcp::VerificationNote(
4324 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC
4325 ).set_cpl_id(cpl->id()),
4326 dcp::VerificationNote(
4327 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC
4328 ).set_cpl_id(cpl->id()),
4329 dcp::VerificationNote(
4330 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_path)
4331 ).set_cpl_id(cpl->id()),
4332 dcp::VerificationNote(
4333 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id(), canonical(pkl)
4339 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_unencrypted_content)
4341 path dir = "build/test/verify_unsigned_pkl_with_unencrypted_content";
4342 prepare_directory (dir);
4343 for (auto i: directory_iterator("test/ref/DCP/dcp_test1")) {
4344 copy_file (i.path(), dir / i.path().filename());
4348 Editor e (dir / dcp_test1_pkl());
4349 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
4352 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
4354 check_verify_result(
4358 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4359 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4360 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4361 dcp::VerificationNote(
4362 dcp::VerificationNote::Type::OK,
4363 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4364 string{"1998x1080"},
4365 canonical(cpl->file().get())
4366 ).set_cpl_id(cpl->id()),
4367 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4368 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4373 BOOST_AUTO_TEST_CASE (verify_partially_encrypted)
4375 path dir ("build/test/verify_must_not_be_partially_encrypted");
4376 prepare_directory (dir);
4380 auto signer = make_shared<dcp::CertificateChain>();
4381 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
4382 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
4383 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
4384 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
4386 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
4390 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
4393 auto writer = mp->start_write(dir / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
4394 dcp::ArrayData j2c ("test/data/flat_red.j2c");
4395 for (int i = 0; i < 24; ++i) {
4396 writer->write (j2c.data(), j2c.size());
4398 writer->finalize ();
4400 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
4402 auto reel = make_shared<dcp::Reel>(
4403 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
4404 make_shared<dcp::ReelSoundAsset>(ms, 0)
4407 reel->add (simple_markers());
4411 cpl->set_content_version (
4412 {"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"}
4414 cpl->set_annotation_text ("A Test DCP");
4415 cpl->set_issuer ("OpenDCP 0.0.25");
4416 cpl->set_creator ("OpenDCP 0.0.25");
4417 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
4418 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,C,R,LFE,-,-"));
4419 cpl->set_main_sound_sample_rate (48000);
4420 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
4421 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
4422 cpl->set_version_number (1);
4426 d.set_issuer("OpenDCP 0.0.25");
4427 d.set_creator("OpenDCP 0.0.25");
4428 d.set_issue_date("2012-07-17T04:45:18+00:00");
4429 d.set_annotation_text("A Test DCP");
4430 d.write_xml(signer);
4432 check_verify_result (
4436 dcp::VerificationNote(
4437 dcp::VerificationNote::Type::OK,
4438 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4439 string{"1440x1080"},
4441 ).set_cpl_id(cpl->id()),
4442 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4443 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4444 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4445 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4446 dcp::VerificationNote(
4447 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED
4448 ).set_cpl_id(cpl->id())
4453 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k)
4455 vector<dcp::VerificationNote> notes;
4456 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"));
4457 auto reader = picture.start_read ();
4458 auto frame = reader->get_frame (0);
4459 verify_j2k(frame, 0, 0, 24, notes);
4460 BOOST_CHECK(notes.empty());
4464 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k)
4466 vector<dcp::VerificationNote> notes;
4467 dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR"));
4468 auto reader = picture.start_read ();
4469 auto frame = reader->get_frame (0);
4470 verify_j2k(frame, 0, 0, 24, notes);
4471 BOOST_CHECK(notes.empty());
4475 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp)
4477 boost::filesystem::path dir = "build/test/verify_jpeg2000_codestream_libdcp";
4478 prepare_directory (dir);
4479 auto dcp = make_simple (dir);
4481 vector<dcp::VerificationNote> notes;
4482 dcp::MonoPictureAsset picture (find_file(dir, "video"));
4483 auto reader = picture.start_read ();
4484 auto frame = reader->get_frame (0);
4485 verify_j2k(frame, 0, 0, 24, notes);
4486 BOOST_CHECK(notes.empty());
4490 /** Check that ResourceID and the XML ID being different is spotted */
4491 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id)
4493 boost::filesystem::path const dir = "build/test/verify_mismatched_subtitle_resource_id";
4494 prepare_directory (dir);
4496 ASDCP::WriterInfo writer_info;
4497 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
4500 auto mxf_id = dcp::make_uuid ();
4501 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
4502 BOOST_REQUIRE (c == Kumu::UUID_Length);
4504 auto resource_id = dcp::make_uuid ();
4505 ASDCP::TimedText::TimedTextDescriptor descriptor;
4506 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
4507 DCP_ASSERT (c == Kumu::UUID_Length);
4509 auto xml_id = dcp::make_uuid ();
4510 ASDCP::TimedText::MXFWriter writer;
4511 auto subs_mxf = dir / "subs.mxf";
4512 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
4513 BOOST_REQUIRE (ASDCP_SUCCESS(r));
4514 writer.WriteTimedTextResource (dcp::String::compose(
4515 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
4516 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
4517 "<Id>urn:uuid:%1</Id>"
4518 "<ContentTitleText>Content</ContentTitleText>"
4519 "<AnnotationText>Annotation</AnnotationText>"
4520 "<IssueDate>2018-10-02T12:25:14</IssueDate>"
4521 "<ReelNumber>1</ReelNumber>"
4522 "<Language>en-US</Language>"
4523 "<EditRate>25 1</EditRate>"
4524 "<TimeCodeRate>25</TimeCodeRate>"
4525 "<StartTime>00:00:00:00</StartTime>"
4526 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
4528 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
4529 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
4530 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
4539 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
4540 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
4542 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
4544 check_verify_result (
4548 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4549 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
4550 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4551 dcp::VerificationNote(
4552 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf)
4553 ).set_cpl_id(cpl->id()),
4554 dcp::VerificationNote(
4555 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID
4556 ).set_cpl_id(cpl->id()),
4557 dcp::VerificationNote(
4558 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
4559 ).set_cpl_id(cpl->id()),
4560 dcp::VerificationNote(
4561 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
4562 ).set_cpl_id(cpl->id())
4567 /** Check that ResourceID and the MXF ID being the same is spotted */
4568 BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id)
4570 boost::filesystem::path const dir = "build/test/verify_incorrect_timed_text_id";
4571 prepare_directory (dir);
4573 ASDCP::WriterInfo writer_info;
4574 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
4577 auto mxf_id = dcp::make_uuid ();
4578 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
4579 BOOST_REQUIRE (c == Kumu::UUID_Length);
4581 auto resource_id = mxf_id;
4582 ASDCP::TimedText::TimedTextDescriptor descriptor;
4583 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
4584 DCP_ASSERT (c == Kumu::UUID_Length);
4586 auto xml_id = resource_id;
4587 ASDCP::TimedText::MXFWriter writer;
4588 auto subs_mxf = dir / "subs.mxf";
4589 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
4590 BOOST_REQUIRE (ASDCP_SUCCESS(r));
4591 writer.WriteTimedTextResource (dcp::String::compose(
4592 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
4593 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
4594 "<Id>urn:uuid:%1</Id>"
4595 "<ContentTitleText>Content</ContentTitleText>"
4596 "<AnnotationText>Annotation</AnnotationText>"
4597 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
4598 "<ReelNumber>1</ReelNumber>"
4599 "<Language>en-US</Language>"
4600 "<EditRate>25 1</EditRate>"
4601 "<TimeCodeRate>25</TimeCodeRate>"
4602 "<StartTime>00:00:00:00</StartTime>"
4603 "<LoadFont ID=\"font\">urn:uuid:0ce6e0ba-58b9-4344-8929-4d9c959c2d55</LoadFont>"
4605 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
4606 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
4607 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
4616 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
4617 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
4619 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
4621 check_verify_result (
4625 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4626 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
4627 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4628 dcp::VerificationNote(
4629 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf)
4630 ).set_cpl_id(cpl->id()),
4631 dcp::VerificationNote(
4632 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID
4633 ).set_cpl_id(cpl->id()),
4634 dcp::VerificationNote(
4635 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
4636 ).set_cpl_id(cpl->id()),
4637 dcp::VerificationNote(
4638 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->file().get()
4639 ).set_cpl_id(cpl->id()),
4640 dcp::VerificationNote(
4641 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"}
4642 ).set_cpl_id(cpl->id())
4647 /** Check a DCP with a 3D asset marked as 2D */
4648 BOOST_AUTO_TEST_CASE (verify_threed_marked_as_twod)
4650 auto const path = private_test / "data" / "xm";
4652 auto cpl = std::make_shared<dcp::CPL>(find_prefix(path, "CPL_"));
4655 check_verify_result (
4659 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4660 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "0d6f57e6-adac-4e1d-bfbe-d162bf13e2cd_j2c.mxf"), cpl),
4661 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "0d6f57e6-adac-4e1d-bfbe-d162bf13e2cd_j2c.mxf"), cpl),
4662 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4663 dcp::VerificationNote(
4664 dcp::VerificationNote::Type::WARNING,
4665 dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(path, "j2c"))
4667 dcp::VerificationNote(
4668 dcp::VerificationNote::Type::BV21_ERROR,
4669 dcp::VerificationNote::Code::INVALID_STANDARD
4676 BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers)
4678 path dir = "build/test/verify_unexpected_things_in_main_markers";
4679 prepare_directory (dir);
4680 auto dcp = make_simple (dir, 1, 24);
4683 HashCalculator calc(find_cpl(dir));
4686 Editor e (find_cpl(dir));
4688 " <IntrinsicDuration>24</IntrinsicDuration>",
4689 "<EntryPoint>0</EntryPoint><Duration>24</Duration>"
4693 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
4695 check_verify_result (
4699 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4700 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4701 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4702 dcp::VerificationNote(
4703 dcp::VerificationNote::Type::OK,
4704 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4705 string{"1998x1080"},
4706 canonical(cpl->file().get())
4707 ).set_cpl_id(cpl->id()),
4708 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4709 dcp::VerificationNote(
4710 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4711 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4712 dcp::VerificationNote(
4713 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT
4714 ).set_cpl_id(cpl->id()),
4715 dcp::VerificationNote(
4716 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION
4717 ).set_cpl_id(cpl->id())
4722 BOOST_AUTO_TEST_CASE(verify_invalid_content_kind)
4724 path dir = "build/test/verify_invalid_content_kind";
4725 prepare_directory (dir);
4726 auto dcp = make_simple (dir, 1, 24);
4729 HashCalculator calc(find_cpl(dir));
4732 Editor e(find_cpl(dir));
4733 e.replace("trailer", "trip");
4736 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4738 check_verify_result (
4742 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4743 dcp::VerificationNote(
4744 dcp::VerificationNote::Type::OK,
4745 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4746 string{"1998x1080"},
4747 canonical(cpl->file().get())
4748 ).set_cpl_id(cpl->id()),
4749 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4750 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4751 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4752 dcp::VerificationNote(
4753 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4754 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4755 dcp::VerificationNote(
4756 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip")
4757 ).set_cpl_id(cpl->id()),
4763 BOOST_AUTO_TEST_CASE(verify_valid_content_kind)
4765 path dir = "build/test/verify_valid_content_kind";
4766 prepare_directory (dir);
4767 auto dcp = make_simple (dir, 1, 24);
4770 HashCalculator calc(find_cpl(dir));
4773 Editor e(find_cpl(dir));
4774 e.replace("<ContentKind>trailer</ContentKind>", "<ContentKind scope=\"http://bobs.contents/\">trip</ContentKind>");
4777 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4779 check_verify_result (
4783 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4784 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4785 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4786 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4787 dcp::VerificationNote(
4788 dcp::VerificationNote::Type::OK,
4789 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4790 string{"1998x1080"},
4791 canonical(cpl->file().get())
4792 ).set_cpl_id(cpl->id()),
4793 dcp::VerificationNote(
4794 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4795 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4800 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1)
4802 path dir = "build/test/verify_invalid_main_picture_active_area_1";
4803 prepare_directory(dir);
4804 auto dcp = make_simple(dir, 1, 24);
4807 auto constexpr area = "<meta:MainPictureActiveArea>";
4809 HashCalculator calc(find_cpl(dir));
4812 Editor e(find_cpl(dir));
4813 e.delete_lines_after(area, 2);
4814 e.insert(area, "<meta:Height>4080</meta:Height>");
4815 e.insert(area, "<meta:Width>1997</meta:Width>");
4818 dcp::PKL pkl(find_pkl(dir));
4819 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4821 check_verify_result(
4825 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4826 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4827 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4828 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4829 dcp::VerificationNote(
4830 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4831 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4832 dcp::VerificationNote(
4833 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir))
4834 ).set_cpl_id(cpl->id()),
4835 dcp::VerificationNote(
4836 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))
4837 ).set_cpl_id(cpl->id()),
4842 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2)
4844 path dir = "build/test/verify_invalid_main_picture_active_area_2";
4845 prepare_directory(dir);
4846 auto dcp = make_simple(dir, 1, 24);
4849 auto constexpr area = "<meta:MainPictureActiveArea>";
4851 HashCalculator calc(find_cpl(dir));
4854 Editor e(find_cpl(dir));
4855 e.delete_lines_after(area, 2);
4856 e.insert(area, "<meta:Height>5125</meta:Height>");
4857 e.insert(area, "<meta:Width>9900</meta:Width>");
4860 dcp::PKL pkl(find_pkl(dir));
4861 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4863 check_verify_result(
4867 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4868 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4869 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
4870 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
4871 dcp::VerificationNote(
4872 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, canonical(find_cpl(dir))
4873 ).set_cpl_id(cpl->id()).set_reference_hash(calc.old_hash()).set_calculated_hash(calc.new_hash()),
4874 dcp::VerificationNote(
4875 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir))
4876 ).set_cpl_id(cpl->id()),
4877 dcp::VerificationNote(
4878 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))
4879 ).set_cpl_id(cpl->id()),
4880 dcp::VerificationNote(
4881 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))
4882 ).set_cpl_id(cpl->id())
4887 BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids)
4891 path dir = "build/test/verify_duplicate_pkl_asset_ids";
4892 prepare_directory(dir);
4893 auto dcp = make_simple(dir, 1, 24);
4897 Editor e(find_pkl(dir));
4898 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab");
4901 dcp::PKL pkl(find_pkl(dir));
4902 auto cpl = std::make_shared<dcp::CPL>(find_cpl(dir));
4904 check_verify_result(
4908 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4909 dcp::VerificationNote(
4910 dcp::VerificationNote::Type::OK,
4911 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4912 string{"1998x1080"},
4913 canonical(cpl->file().get())
4914 ).set_cpl_id(cpl->id()),
4915 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4916 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4917 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) },
4922 BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids)
4926 path dir = "build/test/verify_duplicate_assetmap_asset_ids";
4927 prepare_directory(dir);
4928 auto dcp = make_simple(dir, 1, 24);
4932 Editor e(find_asset_map(dir));
4933 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa");
4936 dcp::PKL pkl(find_pkl(dir));
4937 dcp::AssetMap asset_map(find_asset_map(dir));
4938 auto cpl = make_shared<dcp::CPL>(find_cpl(dir));
4940 check_verify_result(
4944 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
4945 dcp::VerificationNote(
4946 dcp::VerificationNote::Type::OK,
4947 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
4948 string{"1998x1080"},
4949 canonical(cpl->file().get())
4950 ).set_cpl_id(cpl->id()),
4951 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
4952 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
4953 dcp::VerificationNote(
4954 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir))
4956 dcp::VerificationNote(
4957 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54")
4963 BOOST_AUTO_TEST_CASE(verify_mismatched_sound_channel_counts)
4965 boost::filesystem::path const path = "build/test/verify_mismatched_sound_channel_counts";
4967 dcp::MXFMetadata mxf_meta;
4968 mxf_meta.company_name = "OpenDCP";
4969 mxf_meta.product_name = "OpenDCP";
4970 mxf_meta.product_version = "0.0.25";
4972 auto constexpr sample_rate = 48000;
4973 auto constexpr frames = 240;
4975 boost::filesystem::remove_all(path);
4976 boost::filesystem::create_directories(path);
4977 auto dcp = make_shared<dcp::DCP>(path);
4978 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
4979 cpl->set_annotation_text("hello");
4980 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R"));
4981 cpl->set_main_sound_sample_rate(sample_rate);
4982 cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
4983 cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
4984 cpl->set_version_number(1);
4988 /* Reel with 2 channels of audio */
4990 auto mp = simple_picture(path, "1", frames, {});
4991 auto ms = simple_sound(path, "1", mxf_meta, "en-US", frames, sample_rate, {}, 2);
4993 auto reel = make_shared<dcp::Reel>(
4994 std::make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
4995 std::make_shared<dcp::ReelSoundAsset>(ms, 0)
4998 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), frames);
4999 markers->set(dcp::Marker::FFOC, dcp::Time(0, 0, 0, 1, 24));
5006 /* Reel with 6 channels of audio */
5008 auto mp = simple_picture(path, "2", frames, {});
5009 auto ms = simple_sound(path, "2", mxf_meta, "en-US", frames, sample_rate, {}, 6);
5011 auto reel = make_shared<dcp::Reel>(
5012 std::make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
5013 std::make_shared<dcp::ReelSoundAsset>(ms, 0)
5016 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), frames);
5017 markers->set(dcp::Marker::LFOC, dcp::Time(0, 0, 0, frames - 1, 24));
5024 dcp->set_annotation_text("hello");
5027 check_verify_result(
5031 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5032 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5033 dcp::VerificationNote(
5034 dcp::VerificationNote::Type::OK,
5035 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5036 string{"1998x1080"},
5038 ).set_cpl_id(cpl->id()),
5039 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
5040 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
5041 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
5042 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video2.mxf"), cpl),
5043 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video2.mxf"), cpl),
5044 dcp::VerificationNote(
5045 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_CHANNEL_COUNTS, canonical(find_file(path, "audio2"))
5046 ).set_cpl_id(cpl->id())
5051 BOOST_AUTO_TEST_CASE(verify_invalid_main_sound_configuration)
5053 boost::filesystem::path const path = "build/test/verify_invalid_main_sound_configuration";
5055 dcp::MXFMetadata mxf_meta;
5056 mxf_meta.company_name = "OpenDCP";
5057 mxf_meta.product_name = "OpenDCP";
5058 mxf_meta.product_version = "0.0.25";
5060 auto constexpr sample_rate = 48000;
5061 auto constexpr frames = 240;
5063 boost::filesystem::remove_all(path);
5064 boost::filesystem::create_directories(path);
5065 auto dcp = make_shared<dcp::DCP>(path);
5066 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
5067 cpl->set_annotation_text("hello");
5068 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R,C,LFE,Ls,Rs"));
5069 cpl->set_main_sound_sample_rate(sample_rate);
5070 cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
5071 cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
5072 cpl->set_version_number(1);
5074 auto mp = simple_picture(path, "1", frames, {});
5075 auto ms = simple_sound(path, "1", mxf_meta, "en-US", frames, sample_rate, {}, 2);
5077 auto reel = make_shared<dcp::Reel>(
5078 std::make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
5079 std::make_shared<dcp::ReelSoundAsset>(ms, 0)
5082 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), frames);
5083 markers->set(dcp::Marker::FFOC, dcp::Time(0, 0, 0, 1, 24));
5084 markers->set(dcp::Marker::LFOC, dcp::Time(0, 0, 9, 23, 24));
5090 dcp->set_annotation_text("hello");
5093 check_verify_result(
5097 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5098 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5099 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
5100 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video1.mxf"), cpl),
5101 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(path / "video1.mxf"), cpl),
5102 dcp::VerificationNote(
5103 dcp::VerificationNote::Type::OK,
5104 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5105 string{"1998x1080"},
5107 ).set_cpl_id(cpl->id()),
5108 dcp::VerificationNote(
5109 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))
5110 ).set_cpl_id(cpl->id())
5115 BOOST_AUTO_TEST_CASE(verify_invalid_tile_part_size)
5117 boost::filesystem::path const path = "build/test/verify_invalid_tile_part_size";
5118 auto constexpr video_frames = 24;
5119 auto constexpr sample_rate = 48000;
5121 boost::filesystem::remove_all(path);
5122 boost::filesystem::create_directories(path);
5124 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE);
5125 auto picture_writer = mp->start_write(path / "video.mxf", dcp::PictureAsset::Behaviour::MAKE_NEW);
5127 dcp::Size const size(1998, 1080);
5128 auto image = make_shared<dcp::OpenJPEGImage>(size);
5129 boost::random::mt19937 rng(1);
5130 boost::random::uniform_int_distribution<> dist(0, 4095);
5131 for (int c = 0; c < 3; ++c) {
5132 for (int p = 0; p < (1998 * 1080); ++p) {
5133 image->data(c)[p] = dist(rng);
5136 auto j2c = dcp::compress_j2k(image, 750000000, video_frames, false, false);
5137 for (int i = 0; i < 24; ++i) {
5138 picture_writer->write(j2c.data(), j2c.size());
5140 picture_writer->finalize();
5142 auto dcp = make_shared<dcp::DCP>(path);
5143 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
5144 cpl->set_content_version(
5145 dcp::ContentVersion("urn:uuid:75ac29aa-42ac-1234-ecae-49251abefd11", "content-version-label-text")
5147 cpl->set_main_sound_configuration(dcp::MainSoundConfiguration("51/L,R,C,LFE,Ls,Rs"));
5148 cpl->set_main_sound_sample_rate(sample_rate);
5149 cpl->set_main_picture_stored_area(dcp::Size(1998, 1080));
5150 cpl->set_main_picture_active_area(dcp::Size(1998, 1080));
5151 cpl->set_version_number(1);
5153 auto ms = simple_sound(path, "", dcp::MXFMetadata(), "en-US", video_frames, sample_rate, {});
5155 auto reel = make_shared<dcp::Reel>(
5156 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
5157 make_shared<dcp::ReelSoundAsset>(ms, 0)
5162 dcp->set_annotation_text("A Test DCP");
5165 vector<dcp::VerificationNote> expected = {
5166 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5167 dcp::VerificationNote(
5168 dcp::VerificationNote::Type::OK,
5169 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5170 string{"1998x1080"},
5172 ).set_cpl_id(cpl->id()),
5173 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(path / "video.mxf"), cpl),
5174 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
5175 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5176 dcp::VerificationNote(
5177 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC
5178 ).set_cpl_id(cpl->id()),
5179 dcp::VerificationNote(
5180 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC
5181 ).set_cpl_id(cpl->id())
5184 for (auto frame = 0; frame < 24; frame++) {
5186 dcp::VerificationNote(
5187 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(path / "video.mxf")
5188 ).set_frame(frame).set_frame_rate(24).set_cpl_id(cpl->id())
5192 int component_sizes[] = {
5198 for (auto frame = 0; frame < 24; frame++) {
5199 for (auto component = 0; component < 3; component++) {
5201 dcp::VerificationNote(
5202 dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_TILE_PART_SIZE
5203 ).set_frame(frame).set_component(component).set_size(component_sizes[component]).set_cpl_id(cpl->id())
5208 check_verify_result({ path }, {}, expected);
5212 BOOST_AUTO_TEST_CASE(verify_too_many_subtitle_namespaces)
5214 boost::filesystem::path const dir = "test/ref/DCP/subtitle_namespace_test";
5217 BOOST_REQUIRE(!dcp.cpls().empty());
5218 auto cpl = dcp.cpls()[0];
5220 check_verify_result(
5224 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5225 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5226 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"Dcp_FTR-1_F_XX-XX_MOS_2K_20230407_SMPTE_OV"}, cpl),
5227 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "j2c_42b34dcd-caa5-4c7b-aa0f-66a590947ba1.mxf"), cpl),
5228 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "j2c_42b34dcd-caa5-4c7b-aa0f-66a590947ba1.mxf"), cpl),
5229 dcp::VerificationNote(
5230 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE
5231 ).set_cpl_id(cpl->id()),
5232 dcp::VerificationNote(
5233 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE
5234 ).set_cpl_id(cpl->id()),
5235 dcp::VerificationNote(
5236 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME
5237 ).set_cpl_id(cpl->id()),
5238 dcp::VerificationNote(
5239 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(find_file(dir, "sub_"))
5240 ).set_cpl_id(cpl->id()),
5241 dcp::VerificationNote(
5242 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(find_file(dir, "cpl_"))
5243 ).set_cpl_id(cpl->id()),
5244 dcp::VerificationNote(
5245 dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_SUBTITLE_NAMESPACE_COUNT, std::string{"315de731-1173-484c-9a35-bdacf5a9d99d"}
5246 ).set_cpl_id(cpl->id()),
5251 BOOST_AUTO_TEST_CASE(verify_missing_load_font_for_font)
5253 path const dir("build/test/verify_missing_load_font");
5254 prepare_directory (dir);
5255 copy_file ("test/data/subs1.xml", dir / "subs.xml");
5257 Editor editor(dir / "subs.xml");
5258 editor.delete_first_line_containing("LoadFont");
5260 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
5261 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
5262 auto cpl = write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
5264 check_verify_result (
5268 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5269 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5270 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
5271 dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT_FOR_FONT).set_id("theFontId").set_cpl_id(cpl->id())
5277 BOOST_AUTO_TEST_CASE(verify_missing_load_font)
5279 boost::filesystem::path const dir = "build/test/verify_missing_load_font";
5280 prepare_directory(dir);
5281 auto dcp = make_simple (dir, 1, 202);
5284 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
5285 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\">"
5286 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
5287 "<ContentTitleText>Content</ContentTitleText>"
5288 "<AnnotationText>Annotation</AnnotationText>"
5289 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
5290 "<ReelNumber>1</ReelNumber>"
5291 "<EditRate>24 1</EditRate>"
5292 "<TimeCodeRate>24</TimeCodeRate>"
5293 "<StartTime>00:00:00:00</StartTime>"
5294 "<Language>de-DE</Language>"
5296 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
5297 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:06:00\" TimeOut=\"00:00:08:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
5298 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
5304 dcp::File xml_file(dir / "subs.xml", "w");
5305 BOOST_REQUIRE(xml_file);
5306 xml_file.write(xml.c_str(), xml.size(), 1);
5308 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
5309 subs->write(dir / "subs.mxf");
5311 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 202, 0);
5312 auto cpl = dcp->cpls()[0];
5313 cpl->reels()[0]->add(reel_subs);
5316 check_verify_result (
5320 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5321 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5322 dcp::VerificationNote(
5323 dcp::VerificationNote::Type::OK,
5324 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5325 string{"1998x1080"},
5327 ).set_cpl_id(cpl->id()),
5328 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
5329 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
5330 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
5331 dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_LOAD_FONT).set_id(reel_subs->id()).set_cpl_id(cpl->id())
5336 BOOST_AUTO_TEST_CASE(verify_spots_wrong_asset)
5338 boost::filesystem::path const dir = "build/test/verify_spots_wrong_asset";
5339 boost::filesystem::remove_all(dir);
5341 auto dcp1 = make_simple(dir / "1");
5343 auto cpl = dcp1->cpls()[0];
5345 auto const asset_1 = dcp::MonoPictureAsset(dir / "1" / "video.mxf").id();
5347 auto dcp2 = make_simple(dir / "2");
5349 auto const asset_2 = dcp::MonoPictureAsset(dir / "2" / "video.mxf").id();
5351 boost::filesystem::remove(dir / "1" / "video.mxf");
5352 boost::filesystem::copy_file(dir / "2" / "video.mxf", dir / "1" / "video.mxf");
5354 check_verify_result(
5358 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5359 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5360 dcp::VerificationNote(
5361 dcp::VerificationNote::Type::OK,
5362 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5363 string{"1998x1080"},
5365 ).set_cpl_id(cpl->id()),
5366 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
5367 dcp::VerificationNote(dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_MAP_ID).set_id(asset_1).set_other_id(asset_2)
5372 BOOST_AUTO_TEST_CASE(verify_cpl_content_version_label_text_empty)
5374 boost::filesystem::path const dir = "build/test/verify_cpl_content_version_label_text_empty";
5375 boost::filesystem::remove_all(dir);
5377 auto dcp = make_simple(dir);
5378 BOOST_REQUIRE(dcp->cpls().size() == 1);
5379 auto cpl = dcp->cpls()[0];
5380 cpl->set_content_version(dcp::ContentVersion(""));
5383 check_verify_result(
5387 dcp::VerificationNote(
5388 dcp::VerificationNote::Type::OK,
5389 dcp::VerificationNote::Code::VALID_MAIN_PICTURE_ACTIVE_AREA,
5390 string{"1998x1080"},
5392 ).set_cpl_id(cpl->id()),
5393 ok(dcp::VerificationNote::Code::NONE_ENCRYPTED, cpl),
5394 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5395 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"A Test DCP"}, cpl),
5396 ok(dcp::VerificationNote::Code::CORRECT_PICTURE_HASH, canonical(dir / "video.mxf"), cpl),
5397 ok(dcp::VerificationNote::Code::VALID_PICTURE_FRAME_SIZES_IN_BYTES, canonical(dir / "video.mxf"), cpl),
5398 dcp::VerificationNote(dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_CONTENT_VERSION_LABEL_TEXT, cpl->file().get()).set_cpl_id(cpl->id())
5403 /** Check that we don't get any strange errors when verifying encrypted DCPs (DoM #2659) */
5404 BOOST_AUTO_TEST_CASE(verify_encrypted_smpte_dcp)
5406 auto const dir = path("build/test/verify_encrypted_smpte_dcp");
5408 auto key_id = dcp::make_uuid();
5409 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset>(dir, {{ 4 * 24, 5 * 24 }}, key, key_id);
5411 dcp::DecryptedKDM kdm(dcp::LocalTime(), dcp::LocalTime(), "", "", "");
5412 kdm.add_key(dcp::DecryptedKDMKey(string{"MDIK"}, key_id, key, cpl->id(), dcp::Standard::SMPTE));
5414 path const pkl_file = find_file(dir, "pkl_");
5415 path const cpl_file = find_file(dir, "cpl_");
5417 check_verify_result(
5421 ok(dcp::VerificationNote::Code::MATCHING_CPL_HASHES, cpl),
5422 ok(dcp::VerificationNote::Code::VALID_CPL_ANNOTATION_TEXT, string{"hello"}, cpl),
5423 ok(dcp::VerificationNote::Code::ALL_ENCRYPTED, cpl),
5424 dcp::VerificationNote(
5425 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, canonical(cpl_file)
5426 ).set_cpl_id(cpl->id()),
5427 dcp::VerificationNote(
5428 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, canonical(cpl_file)
5429 ).set_cpl_id(cpl->id()),
5430 dcp::VerificationNote(
5431 dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, filename_to_id(pkl_file.filename()), canonical(pkl_file)