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/test/unit_test.hpp>
62 #include <boost/algorithm/string.hpp>
69 using std::make_shared;
71 using std::shared_ptr;
74 using boost::optional;
75 using namespace boost::filesystem;
78 static list<pair<string, optional<path>>> stages;
80 static string filename_to_id(boost::filesystem::path path)
82 return path.string().substr(4, path.string().length() - 8);
85 static boost::filesystem::path const dcp_test1_pkl = find_file("test/ref/DCP/dcp_test1", "pkl_").filename();
86 static string const dcp_test1_pkl_id = filename_to_id(dcp_test1_pkl);
88 static boost::filesystem::path const dcp_test1_cpl = find_file("test/ref/DCP/dcp_test1", "cpl_").filename();
89 static string const dcp_test1_cpl_id = filename_to_id(dcp_test1_cpl);
91 static string const dcp_test1_asset_map_id = "017b3de4-6dda-408d-b19b-6711354b0bc3";
93 static boost::filesystem::path const encryption_test_cpl = find_file("test/ref/DCP/encryption_test", "cpl_").filename();
94 static string const encryption_test_cpl_id = filename_to_id(encryption_test_cpl);
96 static boost::filesystem::path const encryption_test_pkl = find_file("test/ref/DCP/encryption_test", "pkl_").filename();
97 static string const encryption_test_pkl_id = filename_to_id(encryption_test_pkl);
100 stage (string s, optional<path> p)
102 stages.push_back (make_pair (s, p));
112 prepare_directory (path path)
114 using namespace boost::filesystem;
116 create_directories (path);
120 /** Copy dcp_test{reference_number} to build/test/verify_test{verify_test_suffix}
121 * to make a new sacrifical test DCP.
124 setup (int reference_number, string verify_test_suffix)
126 auto const dir = dcp::String::compose("build/test/verify_test%1", verify_test_suffix);
127 prepare_directory (dir);
128 for (auto i: directory_iterator(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number))) {
129 copy_file (i.path(), dir / i.path().filename());
138 write_dcp_with_single_asset (path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::Standard::SMPTE)
140 auto reel = make_shared<dcp::Reel>();
141 reel->add (reel_asset);
142 reel->add (simple_markers());
144 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, standard);
146 auto dcp = make_shared<dcp::DCP>(dir);
148 dcp->set_annotation_text("hello");
155 /** Class that can alter a file by searching and replacing strings within it.
156 * On destruction modifies the file whose name was given to the constructor.
164 _content = dcp::file_to_string (_path);
169 auto f = fopen(_path.string().c_str(), "w");
171 fwrite (_content.c_str(), _content.length(), 1, f);
178 ChangeChecker(Editor* editor)
181 _old_content = _editor->_content;
186 BOOST_REQUIRE(_old_content != _editor->_content);
190 std::string _old_content;
193 void replace (string a, string b)
195 ChangeChecker cc(this);
196 boost::algorithm::replace_all (_content, a, b);
199 void delete_first_line_containing (string s)
201 ChangeChecker cc(this);
202 auto lines = as_lines();
205 for (auto i: lines) {
206 if (i.find(s) == string::npos || done) {
207 _content += i + "\n";
214 void delete_lines (string from, string to)
216 ChangeChecker cc(this);
217 auto lines = as_lines();
218 bool deleting = false;
220 for (auto i: lines) {
221 if (i.find(from) != string::npos) {
225 _content += i + "\n";
227 if (deleting && i.find(to) != string::npos) {
233 void insert (string after, string line)
235 ChangeChecker cc(this);
236 auto lines = as_lines();
238 bool replaced = false;
239 for (auto i: lines) {
240 _content += i + "\n";
241 if (!replaced && i.find(after) != string::npos) {
242 _content += line + "\n";
248 void delete_lines_after(string after, int lines_to_delete)
250 ChangeChecker cc(this);
251 auto lines = as_lines();
253 auto iter = std::find_if(lines.begin(), lines.end(), [after](string const& line) {
254 return line.find(after) != string::npos;
257 for (auto i = lines.begin(); i != lines.end(); ++i) {
259 to_delete = lines_to_delete;
260 _content += *i + "\n";
261 } else if (to_delete == 0) {
262 _content += *i + "\n";
270 friend class ChangeChecker;
272 vector<string> as_lines() const
274 vector<string> lines;
275 boost::algorithm::split(lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on);
280 std::string _content;
284 LIBDCP_DISABLE_WARNINGS
287 dump_notes (vector<dcp::VerificationNote> const & notes)
289 for (auto i: notes) {
290 std::cout << dcp::note_to_string(i) << "\n";
293 LIBDCP_ENABLE_WARNINGS
298 check_verify_result (vector<path> dir, vector<dcp::VerificationNote> test_notes)
300 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
301 std::sort (notes.begin(), notes.end());
302 std::sort (test_notes.begin(), test_notes.end());
304 string message = "\nVerification notes from test:\n";
305 for (auto i: notes) {
306 message += " " + note_to_string(i) + "\n";
307 message += dcp::String::compose(
308 " [%1 %2 %3 %4 %5]\n",
309 static_cast<int>(i.type()),
310 static_cast<int>(i.code()),
311 i.note().get_value_or("<none>"),
312 i.file().get_value_or("<none>"),
313 i.line().get_value_or(0)
316 message += "Expected:\n";
317 for (auto i: test_notes) {
318 message += " " + note_to_string(i) + "\n";
319 message += dcp::String::compose(
320 " [%1 %2 %3 %4 %5]\n",
321 static_cast<int>(i.type()),
322 static_cast<int>(i.code()),
323 i.note().get_value_or("<none>"),
324 i.file().get_value_or("<none>"),
325 i.line().get_value_or(0)
329 BOOST_REQUIRE_MESSAGE (notes == test_notes, message);
333 /* Copy dcp_test1 to build/test/verify_test{suffix} then edit a file found by the functor 'file',
334 * replacing from with to. Verify the resulting DCP and check that the results match the given
339 check_verify_result_after_replace (string suffix, boost::function<path (string)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
341 auto dir = setup (1, suffix);
344 Editor e (file(suffix));
345 e.replace (from, to);
348 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
350 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
351 auto i = notes.begin();
352 auto j = codes.begin();
353 while (i != notes.end()) {
354 BOOST_CHECK_EQUAL (i->code(), *j);
361 BOOST_AUTO_TEST_CASE (verify_no_error)
364 auto dir = setup (1, "no_error");
365 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
367 path const cpl_file = dir / dcp_test1_cpl;
368 path const pkl_file = dir / dcp_test1_pkl;
369 path const assetmap_file = dir / "ASSETMAP.xml";
371 auto st = stages.begin();
372 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
373 BOOST_REQUIRE (st->second);
374 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir));
376 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
377 BOOST_REQUIRE (st->second);
378 BOOST_CHECK_EQUAL (st->second.get(), canonical(cpl_file));
380 BOOST_CHECK_EQUAL (st->first, "Checking reel");
381 BOOST_REQUIRE (!st->second);
383 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
384 BOOST_REQUIRE (st->second);
385 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "video.mxf"));
387 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
388 BOOST_REQUIRE (st->second);
389 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "video.mxf"));
391 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
392 BOOST_REQUIRE (st->second);
393 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "audio.mxf"));
395 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
396 BOOST_REQUIRE (st->second);
397 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "audio.mxf"));
399 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
400 BOOST_REQUIRE (st->second);
401 BOOST_CHECK_EQUAL (st->second.get(), canonical(pkl_file));
403 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
404 BOOST_REQUIRE (st->second);
405 BOOST_CHECK_EQUAL (st->second.get(), canonical(assetmap_file));
407 BOOST_REQUIRE (st == stages.end());
409 BOOST_CHECK_EQUAL (notes.size(), 0U);
413 BOOST_AUTO_TEST_CASE (verify_incorrect_picture_sound_hash)
415 using namespace boost::filesystem;
417 auto dir = setup (1, "incorrect_picture_sound_hash");
419 auto video_path = path(dir / "video.mxf");
420 auto mod = fopen(video_path.string().c_str(), "r+b");
422 fseek (mod, 4096, SEEK_SET);
424 fwrite (&x, sizeof(x), 1, mod);
427 auto audio_path = path(dir / "audio.mxf");
428 mod = fopen(audio_path.string().c_str(), "r+b");
430 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
431 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
434 dcp::ASDCPErrorSuspender sus;
435 check_verify_result (
438 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_PICTURE_HASH, canonical(video_path) },
439 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_SOUND_HASH, canonical(audio_path) },
444 BOOST_AUTO_TEST_CASE (verify_mismatched_picture_sound_hashes)
446 using namespace boost::filesystem;
448 auto dir = setup (1, "mismatched_picture_sound_hashes");
451 Editor e (dir / dcp_test1_pkl);
452 e.replace ("<Hash>", "<Hash>x");
455 check_verify_result (
458 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, dcp_test1_cpl_id, canonical(dir / dcp_test1_cpl) },
459 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_PICTURE_HASHES, canonical(dir / "video.mxf") },
460 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_SOUND_HASHES, canonical(dir / "audio.mxf") },
461 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xKcJb7S2K5cNm8RG4kfQD5FTeS0A=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 28 },
462 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xtfX1mVIKJCVr1m7Y32Nzxf0+Rpw=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 12 },
463 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, "value 'xwUmt8G+cFFKMGt0ueS9+F1S4uhc=' is invalid Base64-encoded binary", canonical(dir / dcp_test1_pkl), 20 },
468 BOOST_AUTO_TEST_CASE (verify_failed_read_content_kind)
470 auto dir = setup (1, "failed_read_content_kind");
473 Editor e (dir / dcp_test1_cpl);
474 e.replace ("<ContentKind>", "<ContentKind>x");
477 check_verify_result (
480 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, dcp_test1_cpl_id, canonical(dir / dcp_test1_cpl) },
481 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("xtrailer") }
490 return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_cpl);
498 return dcp::String::compose("build/test/verify_test%1/%2", suffix, dcp_test1_pkl);
504 asset_map (string suffix)
506 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", suffix);
510 BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_rate)
512 check_verify_result_after_replace (
513 "invalid_picture_frame_rate", &cpl,
514 "<FrameRate>24 1", "<FrameRate>99 1",
515 { dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES,
516 dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE }
520 BOOST_AUTO_TEST_CASE (verify_missing_asset)
522 auto dir = setup (1, "missing_asset");
523 remove (dir / "video.mxf");
524 check_verify_result (
527 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_ASSET, canonical(dir) / "video.mxf" }
532 BOOST_AUTO_TEST_CASE (verify_empty_asset_path)
534 check_verify_result_after_replace (
535 "empty_asset_path", &asset_map,
536 "<Path>video.mxf</Path>", "<Path></Path>",
537 { dcp::VerificationNote::Code::EMPTY_ASSET_PATH }
542 BOOST_AUTO_TEST_CASE (verify_mismatched_standard)
544 check_verify_result_after_replace (
545 "mismatched_standard", &cpl,
546 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
547 { dcp::VerificationNote::Code::MISMATCHED_STANDARD,
548 dcp::VerificationNote::Code::INVALID_XML,
549 dcp::VerificationNote::Code::INVALID_XML,
550 dcp::VerificationNote::Code::INVALID_XML,
551 dcp::VerificationNote::Code::INVALID_XML,
552 dcp::VerificationNote::Code::INVALID_XML,
553 dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES }
558 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_id)
560 /* There's no MISMATCHED_CPL_HASHES error here because it can't find the correct hash by ID (since the ID is wrong) */
561 check_verify_result_after_replace (
562 "invalid_xml_cpl_id", &cpl,
563 "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab", "<Id>urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358a",
564 { dcp::VerificationNote::Code::INVALID_XML }
569 BOOST_AUTO_TEST_CASE (verify_invalid_xml_issue_date)
571 check_verify_result_after_replace (
572 "invalid_xml_issue_date", &cpl,
573 "<IssueDate>", "<IssueDate>x",
574 { dcp::VerificationNote::Code::INVALID_XML,
575 dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES }
580 BOOST_AUTO_TEST_CASE (verify_invalid_xml_pkl_id)
582 check_verify_result_after_replace (
583 "invalid_xml_pkl_id", &pkl,
584 "<Id>urn:uuid:" + dcp_test1_pkl_id.substr(0, 3),
585 "<Id>urn:uuid:x" + dcp_test1_pkl_id.substr(1, 2),
586 { dcp::VerificationNote::Code::INVALID_XML }
591 BOOST_AUTO_TEST_CASE (verify_invalid_xml_asset_map_id)
593 check_verify_result_after_replace (
594 "invalid_xml_asset_map_id", &asset_map,
595 "<Id>urn:uuid:" + dcp_test1_asset_map_id.substr(0, 3),
596 "<Id>urn:uuid:x" + dcp_test1_asset_map_id.substr(1, 2),
597 { dcp::VerificationNote::Code::INVALID_XML }
602 BOOST_AUTO_TEST_CASE (verify_invalid_standard)
605 auto dir = setup (3, "verify_invalid_standard");
606 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
608 path const cpl_file = dir / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
609 path const pkl_file = dir / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
610 path const assetmap_file = dir / "ASSETMAP";
612 auto st = stages.begin();
613 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
614 BOOST_REQUIRE (st->second);
615 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir));
617 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
618 BOOST_REQUIRE (st->second);
619 BOOST_CHECK_EQUAL (st->second.get(), canonical(cpl_file));
621 BOOST_CHECK_EQUAL (st->first, "Checking reel");
622 BOOST_REQUIRE (!st->second);
624 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
625 BOOST_REQUIRE (st->second);
626 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
628 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
629 BOOST_REQUIRE (st->second);
630 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
632 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
633 BOOST_REQUIRE (st->second);
634 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
636 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
637 BOOST_REQUIRE (st->second);
638 BOOST_CHECK_EQUAL (st->second.get(), canonical(dir / "pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
640 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
641 BOOST_REQUIRE (st->second);
642 BOOST_CHECK_EQUAL (st->second.get(), canonical(pkl_file));
644 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
645 BOOST_REQUIRE (st->second);
646 BOOST_CHECK_EQUAL (st->second.get(), canonical(assetmap_file));
648 BOOST_REQUIRE (st == stages.end());
650 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
651 auto i = notes.begin ();
652 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::Type::BV21_ERROR);
653 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::Code::INVALID_STANDARD);
655 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::Type::BV21_ERROR);
656 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K);
659 /* DCP with a short asset */
660 BOOST_AUTO_TEST_CASE (verify_invalid_duration)
662 auto dir = setup (8, "invalid_duration");
663 check_verify_result (
666 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
667 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") },
668 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("d7576dcb-a361-4139-96b8-267f5f8d7f91") },
669 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") },
670 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_INTRINSIC_DURATION, string("a2a87f5d-b749-4a7e-8d0c-9d48a4abf626") },
671 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_GUARD_BITS_FOR_2K, string("2") }
678 dcp_from_frame (dcp::ArrayData const& frame, path dir)
680 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::Standard::SMPTE);
681 create_directories (dir);
682 auto writer = asset->start_write (dir / "pic.mxf", true);
683 for (int i = 0; i < 24; ++i) {
684 writer->write (frame.data(), frame.size());
688 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
689 return write_dcp_with_single_asset (dir, reel_asset);
693 BOOST_AUTO_TEST_CASE (verify_invalid_picture_frame_size_in_bytes)
695 int const too_big = 1302083 * 2;
697 /* Compress a black image */
698 auto image = black_image ();
699 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
700 BOOST_REQUIRE (frame.size() < too_big);
702 /* Place it in a bigger block with some zero padding at the end */
703 dcp::ArrayData oversized_frame(too_big);
704 memcpy (oversized_frame.data(), frame.data(), frame.size());
705 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
707 path const dir("build/test/verify_invalid_picture_frame_size_in_bytes");
708 prepare_directory (dir);
709 auto cpl = dcp_from_frame (oversized_frame, dir);
711 check_verify_result (
714 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte") },
715 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf") },
716 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
721 BOOST_AUTO_TEST_CASE (verify_nearly_invalid_picture_frame_size_in_bytes)
723 int const nearly_too_big = 1302083 * 0.98;
725 /* Compress a black image */
726 auto image = black_image ();
727 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
728 BOOST_REQUIRE (frame.size() < nearly_too_big);
730 /* Place it in a bigger block with some zero padding at the end */
731 dcp::ArrayData oversized_frame(nearly_too_big);
732 memcpy (oversized_frame.data(), frame.data(), frame.size());
733 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
735 path const dir("build/test/verify_nearly_invalid_picture_frame_size_in_bytes");
736 prepare_directory (dir);
737 auto cpl = dcp_from_frame (oversized_frame, dir);
739 check_verify_result (
742 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_JPEG2000_CODESTREAM, string("missing marker start byte") },
743 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_PICTURE_FRAME_SIZE_IN_BYTES, canonical(dir / "pic.mxf") },
744 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
749 BOOST_AUTO_TEST_CASE (verify_valid_picture_frame_size_in_bytes)
751 /* Compress a black image */
752 auto image = black_image ();
753 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
754 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
756 path const dir("build/test/verify_valid_picture_frame_size_in_bytes");
757 prepare_directory (dir);
758 auto cpl = dcp_from_frame (frame, dir);
760 check_verify_result ({ dir }, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
764 BOOST_AUTO_TEST_CASE (verify_valid_interop_subtitles)
766 path const dir("build/test/verify_valid_interop_subtitles");
767 prepare_directory (dir);
768 copy_file ("test/data/subs1.xml", dir / "subs.xml");
769 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
770 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
771 write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
773 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD }});
777 BOOST_AUTO_TEST_CASE (verify_invalid_interop_subtitles)
779 using namespace boost::filesystem;
781 path const dir("build/test/verify_invalid_interop_subtitles");
782 prepare_directory (dir);
783 copy_file ("test/data/subs1.xml", dir / "subs.xml");
784 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
785 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
786 write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
789 Editor e (dir / "subs.xml");
790 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
793 check_verify_result (
796 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
797 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 5 },
799 dcp::VerificationNote::Type::ERROR,
800 dcp::VerificationNote::Code::INVALID_XML,
801 string("element 'Foo' is not allowed for content model '(SubtitleID,MovieTitle,ReelNumber,Language,LoadFont*,Font*,Subtitle*)'"),
809 BOOST_AUTO_TEST_CASE(verify_interop_subtitle_asset_with_no_subtitles)
811 path const dir("build/test/verify_interop_subtitle_asset_with_no_subtitles");
812 prepare_directory(dir);
813 copy_file("test/data/subs4.xml", dir / "subs.xml");
814 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
815 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
816 write_dcp_with_single_asset(dir, reel_asset, dcp::Standard::INTEROP);
818 check_verify_result (
821 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
822 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) },
828 BOOST_AUTO_TEST_CASE (verify_valid_smpte_subtitles)
830 path const dir("build/test/verify_valid_smpte_subtitles");
831 prepare_directory (dir);
832 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
833 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
834 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
835 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
840 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
841 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-04-14T13:19:14.000+02:00"} }
846 BOOST_AUTO_TEST_CASE (verify_invalid_smpte_subtitles)
848 using namespace boost::filesystem;
850 path const dir("build/test/verify_invalid_smpte_subtitles");
851 prepare_directory (dir);
852 /* This broken_smpte.mxf does not use urn:uuid: for its subtitle ID, which we tolerate (rightly or wrongly) */
853 copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
854 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
855 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
856 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
858 check_verify_result (
861 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 2 },
863 dcp::VerificationNote::Type::ERROR,
864 dcp::VerificationNote::Code::INVALID_XML,
865 string("element 'Foo' is not allowed for content model '(Id,ContentTitleText,AnnotationText?,IssueDate,ReelNumber?,Language?,EditRate,TimeCodeRate,StartTime?,DisplayType?,LoadFont*,SubtitleList)'"),
869 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
870 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
871 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2020-05-09T00:29:21.000+02:00"} }
876 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles)
878 path const dir("build/test/verify_empty_text_node_in_subtitles");
879 prepare_directory (dir);
880 copy_file ("test/data/empty_text.mxf", dir / "subs.mxf");
881 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
882 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
883 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
885 check_verify_result (
888 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT },
889 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
890 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") },
891 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
892 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2021-08-09T18:34:46.000+02:00"} }
897 /** A <Text> node with no content except some <Font> nodes, which themselves do have content */
898 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes)
900 path const dir("build/test/verify_empty_text_node_in_subtitles_with_child_nodes");
901 prepare_directory (dir);
902 copy_file ("test/data/empty_but_with_children.xml", dir / "subs.xml");
903 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
904 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
905 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
907 check_verify_result (
910 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
915 /** A <Text> node with no content except some <Font> nodes, which themselves also have no content */
916 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes)
918 path const dir("build/test/verify_empty_text_node_in_subtitles_with_empty_child_nodes");
919 prepare_directory (dir);
920 copy_file ("test/data/empty_with_empty_children.xml", dir / "subs.xml");
921 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
922 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
923 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
925 check_verify_result (
928 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) },
929 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
930 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT },
935 BOOST_AUTO_TEST_CASE (verify_external_asset)
937 path const ov_dir("build/test/verify_external_asset");
938 prepare_directory (ov_dir);
940 auto image = black_image ();
941 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
942 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
943 dcp_from_frame (frame, ov_dir);
945 dcp::DCP ov (ov_dir);
948 path const vf_dir("build/test/verify_external_asset_vf");
949 prepare_directory (vf_dir);
951 auto picture = ov.cpls()[0]->reels()[0]->main_picture();
952 auto cpl = write_dcp_with_single_asset (vf_dir, picture);
954 check_verify_result (
957 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, picture->asset()->id() },
958 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
963 BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata)
965 path const dir("build/test/verify_valid_cpl_metadata");
966 prepare_directory (dir);
968 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
969 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
970 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
972 auto reel = make_shared<dcp::Reel>();
973 reel->add (reel_asset);
975 reel->add (make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 16 * 24), 0));
976 reel->add (simple_markers(16 * 24));
978 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
980 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
981 cpl->set_main_sound_sample_rate (48000);
982 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
983 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
984 cpl->set_version_number (1);
988 dcp.set_annotation_text("hello");
994 find_prefix(path dir, string prefix)
996 auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) {
997 return boost::starts_with(p.filename().string(), prefix);
1000 BOOST_REQUIRE(iter != directory_iterator());
1001 return iter->path();
1005 path find_cpl (path dir)
1007 return find_prefix(dir, "cpl_");
1014 return find_prefix(dir, "pkl_");
1019 find_asset_map(path dir)
1021 return find_prefix(dir, "ASSETMAP");
1025 /* DCP with invalid CompositionMetadataAsset */
1026 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag)
1028 using namespace boost::filesystem;
1030 path const dir("build/test/verify_invalid_cpl_metadata_bad_tag");
1031 prepare_directory (dir);
1033 auto reel = make_shared<dcp::Reel>();
1034 reel->add (black_picture_asset(dir));
1035 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1037 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1038 cpl->set_main_sound_sample_rate (48000);
1039 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1040 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1041 cpl->set_version_number (1);
1043 reel->add (simple_markers());
1047 dcp.set_annotation_text("hello");
1051 Editor e (find_cpl(dir));
1052 e.replace ("MainSound", "MainSoundX");
1055 check_verify_result (
1058 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50 },
1059 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51 },
1061 dcp::VerificationNote::Type::ERROR,
1062 dcp::VerificationNote::Code::INVALID_XML,
1063 string("element 'meta:MainSoundXConfiguration' is not allowed for content model "
1064 "'(Id,AnnotationText?,EditRate,IntrinsicDuration,EntryPoint?,Duration?,"
1065 "FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
1066 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,"
1067 "MainSoundSampleRate,MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,"
1068 "ExtensionMetadataList?,)'"),
1069 canonical(cpl->file().get()),
1072 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) },
1077 /* DCP with invalid CompositionMetadataAsset */
1078 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_missing_tag)
1080 path const dir("build/test/verify_invalid_cpl_metadata_missing_tag");
1081 prepare_directory (dir);
1083 auto reel = make_shared<dcp::Reel>();
1084 reel->add (black_picture_asset(dir));
1085 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1087 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1088 cpl->set_main_sound_sample_rate (48000);
1089 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1090 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1094 dcp.set_annotation_text("hello");
1098 Editor e (find_cpl(dir));
1099 e.replace ("meta:Width", "meta:WidthX");
1102 check_verify_result (
1104 {{ dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::FAILED_READ, string("missing XML tag Width in MainPictureStoredArea") }}
1109 BOOST_AUTO_TEST_CASE (verify_invalid_language1)
1111 path const dir("build/test/verify_invalid_language1");
1112 prepare_directory (dir);
1113 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1114 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1115 asset->_language = "wrong-andbad";
1116 asset->write (dir / "subs.mxf");
1117 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1118 reel_asset->_language = "badlang";
1119 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1121 check_verify_result (
1124 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") },
1125 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") },
1126 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1131 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
1132 BOOST_AUTO_TEST_CASE (verify_invalid_language2)
1134 path const dir("build/test/verify_invalid_language2");
1135 prepare_directory (dir);
1136 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1137 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1138 asset->_language = "wrong-andbad";
1139 asset->write (dir / "subs.mxf");
1140 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1141 reel_asset->_language = "badlang";
1142 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1144 check_verify_result (
1147 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") },
1148 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") },
1149 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1154 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
1155 * the release territory.
1157 BOOST_AUTO_TEST_CASE (verify_invalid_language3)
1159 path const dir("build/test/verify_invalid_language3");
1160 prepare_directory (dir);
1162 auto picture = simple_picture (dir, "foo");
1163 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1164 auto reel = make_shared<dcp::Reel>();
1165 reel->add (reel_picture);
1166 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
1167 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1168 reel->add (reel_sound);
1169 reel->add (simple_markers());
1171 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1173 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
1174 cpl->_additional_subtitle_languages.push_back("andso-is-this");
1175 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1176 cpl->set_main_sound_sample_rate (48000);
1177 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1178 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1179 cpl->set_version_number (1);
1180 cpl->_release_territory = "fred-jim";
1181 auto dcp = make_shared<dcp::DCP>(dir);
1183 dcp->set_annotation_text("hello");
1186 check_verify_result (
1189 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong") },
1190 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this") },
1191 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim") },
1192 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz") },
1198 vector<dcp::VerificationNote>
1199 check_picture_size (int width, int height, int frame_rate, bool three_d)
1201 using namespace boost::filesystem;
1203 path dcp_path = "build/test/verify_picture_test";
1204 prepare_directory (dcp_path);
1206 shared_ptr<dcp::PictureAsset> mp;
1208 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1210 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1212 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
1214 auto image = black_image (dcp::Size(width, height));
1215 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1216 int const length = three_d ? frame_rate * 2 : frame_rate;
1217 for (int i = 0; i < length; ++i) {
1218 picture_writer->write (j2c.data(), j2c.size());
1220 picture_writer->finalize ();
1222 auto d = make_shared<dcp::DCP>(dcp_path);
1223 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1224 cpl->set_annotation_text ("A Test DCP");
1225 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1226 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1227 cpl->set_main_sound_sample_rate (48000);
1228 cpl->set_main_picture_stored_area(dcp::Size(width, height));
1229 cpl->set_main_picture_active_area(dcp::Size(width, height));
1230 cpl->set_version_number (1);
1232 auto reel = make_shared<dcp::Reel>();
1235 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1237 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1240 reel->add (simple_markers(frame_rate));
1245 d->set_annotation_text("A Test DCP");
1248 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1254 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1256 auto notes = check_picture_size(width, height, frame_rate, three_d);
1257 BOOST_CHECK_EQUAL (notes.size(), 0U);
1263 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1265 auto notes = check_picture_size(width, height, frame_rate, three_d);
1266 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1267 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1268 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS);
1274 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1276 auto notes = check_picture_size(width, height, frame_rate, three_d);
1277 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1278 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::Type::BV21_ERROR);
1279 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K);
1285 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1287 auto notes = check_picture_size(width, height, frame_rate, three_d);
1288 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1289 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1290 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K);
1294 BOOST_AUTO_TEST_CASE (verify_picture_size)
1296 using namespace boost::filesystem;
1299 check_picture_size_ok (2048, 858, 24, false);
1300 check_picture_size_ok (2048, 858, 25, false);
1301 check_picture_size_ok (2048, 858, 48, false);
1302 check_picture_size_ok (2048, 858, 24, true);
1303 check_picture_size_ok (2048, 858, 25, true);
1304 check_picture_size_ok (2048, 858, 48, true);
1307 check_picture_size_ok (1998, 1080, 24, false);
1308 check_picture_size_ok (1998, 1080, 25, false);
1309 check_picture_size_ok (1998, 1080, 48, false);
1310 check_picture_size_ok (1998, 1080, 24, true);
1311 check_picture_size_ok (1998, 1080, 25, true);
1312 check_picture_size_ok (1998, 1080, 48, true);
1315 check_picture_size_ok (4096, 1716, 24, false);
1318 check_picture_size_ok (3996, 2160, 24, false);
1320 /* Bad frame size */
1321 check_picture_size_bad_frame_size (2050, 858, 24, false);
1322 check_picture_size_bad_frame_size (2048, 658, 25, false);
1323 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1324 check_picture_size_bad_frame_size (4000, 2000, 24, true);
1326 /* Bad 2K frame rate */
1327 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1328 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1329 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1331 /* Bad 4K frame rate */
1332 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1333 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1336 auto notes = check_picture_size(3996, 2160, 24, true);
1337 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1338 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1339 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D);
1345 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")
1348 std::make_shared<dcp::SubtitleString>(
1356 dcp::Time(start_frame, 24, 24),
1357 dcp::Time(end_frame, 24, 24),
1359 dcp::HAlign::CENTER,
1363 dcp::Direction::LTR,
1375 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes)
1377 path const dir("build/test/verify_invalid_closed_caption_xml_size_in_bytes");
1378 prepare_directory (dir);
1380 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1381 for (int i = 0; i < 2048; ++i) {
1382 add_test_subtitle (asset, i * 24, i * 24 + 20);
1384 asset->set_language (dcp::LanguageTag("de-DE"));
1385 asset->write (dir / "subs.mxf");
1386 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 49148, 0);
1387 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1389 check_verify_result (
1392 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1394 dcp::VerificationNote::Type::BV21_ERROR,
1395 dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES,
1397 canonical(dir / "subs.mxf")
1399 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1400 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1406 shared_ptr<dcp::SMPTESubtitleAsset>
1407 make_large_subtitle_asset (path font_file)
1409 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1410 dcp::ArrayData big_fake_font(1024 * 1024);
1411 big_fake_font.write (font_file);
1412 for (int i = 0; i < 116; ++i) {
1413 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1421 verify_timed_text_asset_too_large (string name)
1423 auto const dir = path("build/test") / name;
1424 prepare_directory (dir);
1425 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1426 add_test_subtitle (asset, 0, 240);
1427 asset->set_language (dcp::LanguageTag("de-DE"));
1428 asset->write (dir / "subs.mxf");
1430 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 240, 0);
1431 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1433 check_verify_result (
1436 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121695532"), canonical(dir / "subs.mxf") },
1437 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, string("121634816"), canonical(dir / "subs.mxf") },
1438 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1439 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1440 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1445 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1447 verify_timed_text_asset_too_large<dcp::ReelSMPTESubtitleAsset>("verify_subtitle_asset_too_large");
1448 verify_timed_text_asset_too_large<dcp::ReelSMPTEClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1452 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language)
1454 path dir = "build/test/verify_missing_subtitle_language";
1455 prepare_directory (dir);
1456 auto dcp = make_simple (dir, 1, 106);
1459 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1460 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1461 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1462 "<ContentTitleText>Content</ContentTitleText>"
1463 "<AnnotationText>Annotation</AnnotationText>"
1464 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1465 "<ReelNumber>1</ReelNumber>"
1466 "<EditRate>24 1</EditRate>"
1467 "<TimeCodeRate>24</TimeCodeRate>"
1468 "<StartTime>00:00:00:00</StartTime>"
1469 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1471 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1472 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1473 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1479 dcp::File xml_file(dir / "subs.xml", "w");
1480 BOOST_REQUIRE (xml_file);
1481 xml_file.write(xml.c_str(), xml.size(), 1);
1483 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1484 subs->write (dir / "subs.mxf");
1486 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1487 dcp->cpls()[0]->reels()[0]->add(reel_subs);
1488 dcp->set_annotation_text("A Test DCP");
1491 check_verify_result (
1494 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") },
1495 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1500 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages)
1502 path path ("build/test/verify_mismatched_subtitle_languages");
1503 auto constexpr reel_length = 192;
1504 auto dcp = make_simple (path, 2, reel_length);
1505 auto cpl = dcp->cpls()[0];
1508 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1509 subs->set_language (dcp::LanguageTag("de-DE"));
1510 subs->add (simple_subtitle());
1511 subs->write (path / "subs1.mxf");
1512 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
1513 cpl->reels()[0]->add(reel_subs);
1517 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1518 subs->set_language (dcp::LanguageTag("en-US"));
1519 subs->add (simple_subtitle());
1520 subs->write (path / "subs2.mxf");
1521 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
1522 cpl->reels()[1]->add(reel_subs);
1525 dcp->set_annotation_text("A Test DCP");
1528 check_verify_result (
1531 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") },
1532 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") },
1533 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES }
1538 BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed)
1540 path path ("build/test/verify_multiple_closed_caption_languages_allowed");
1541 auto constexpr reel_length = 192;
1542 auto dcp = make_simple (path, 2, reel_length);
1543 auto cpl = dcp->cpls()[0];
1546 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
1547 ccaps->set_language (dcp::LanguageTag("de-DE"));
1548 ccaps->add (simple_subtitle());
1549 ccaps->write (path / "subs1.mxf");
1550 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
1551 cpl->reels()[0]->add(reel_ccaps);
1555 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
1556 ccaps->set_language (dcp::LanguageTag("en-US"));
1557 ccaps->add (simple_subtitle());
1558 ccaps->write (path / "subs2.mxf");
1559 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
1560 cpl->reels()[1]->add(reel_ccaps);
1563 dcp->set_annotation_text("A Test DCP");
1566 check_verify_result (
1569 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") },
1570 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") }
1575 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time)
1577 path dir = "build/test/verify_missing_subtitle_start_time";
1578 prepare_directory (dir);
1579 auto dcp = make_simple (dir, 1, 106);
1582 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1583 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1584 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1585 "<ContentTitleText>Content</ContentTitleText>"
1586 "<AnnotationText>Annotation</AnnotationText>"
1587 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1588 "<ReelNumber>1</ReelNumber>"
1589 "<Language>de-DE</Language>"
1590 "<EditRate>24 1</EditRate>"
1591 "<TimeCodeRate>24</TimeCodeRate>"
1592 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1594 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1595 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1596 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1602 dcp::File xml_file(dir / "subs.xml", "w");
1603 BOOST_REQUIRE (xml_file);
1604 xml_file.write(xml.c_str(), xml.size(), 1);
1606 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1607 subs->write (dir / "subs.mxf");
1609 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1610 dcp->cpls()[0]->reels()[0]->add(reel_subs);
1611 dcp->set_annotation_text("A Test DCP");
1614 check_verify_result (
1617 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1618 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1623 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time)
1625 path dir = "build/test/verify_invalid_subtitle_start_time";
1626 prepare_directory (dir);
1627 auto dcp = make_simple (dir, 1, 106);
1630 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1631 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1632 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1633 "<ContentTitleText>Content</ContentTitleText>"
1634 "<AnnotationText>Annotation</AnnotationText>"
1635 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1636 "<ReelNumber>1</ReelNumber>"
1637 "<Language>de-DE</Language>"
1638 "<EditRate>24 1</EditRate>"
1639 "<TimeCodeRate>24</TimeCodeRate>"
1640 "<StartTime>00:00:02:00</StartTime>"
1641 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1643 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1644 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1645 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1651 dcp::File xml_file(dir / "subs.xml", "w");
1652 BOOST_REQUIRE (xml_file);
1653 xml_file.write(xml.c_str(), xml.size(), 1);
1655 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1656 subs->write (dir / "subs.mxf");
1658 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1659 dcp->cpls().front()->reels().front()->add(reel_subs);
1660 dcp->set_annotation_text("A Test DCP");
1663 check_verify_result (
1666 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1667 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1675 TestText (int in_, int out_, float v_position_ = 0, dcp::VAlign v_align_ = dcp::VAlign::CENTER, string text_ = "Hello")
1678 , v_position(v_position_)
1686 dcp::VAlign v_align;
1692 shared_ptr<dcp::CPL>
1693 dcp_with_text (path dir, vector<TestText> subs)
1695 prepare_directory (dir);
1696 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1697 asset->set_start_time (dcp::Time());
1698 for (auto i: subs) {
1699 add_test_subtitle (asset, i.in, i.out, i.v_position, i.v_align, i.text);
1701 asset->set_language (dcp::LanguageTag("de-DE"));
1702 asset->write (dir / "subs.mxf");
1704 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
1705 return write_dcp_with_single_asset (dir, reel_asset);
1710 shared_ptr<dcp::CPL>
1711 dcp_with_text_from_file (path dir, boost::filesystem::path subs_xml)
1713 prepare_directory (dir);
1714 auto asset = make_shared<dcp::SMPTESubtitleAsset>(subs_xml);
1715 asset->set_start_time (dcp::Time());
1716 asset->set_language (dcp::LanguageTag("de-DE"));
1718 auto subs_mxf = dir / "subs.mxf";
1719 asset->write (subs_mxf);
1721 /* The call to write() puts the asset into the DCP correctly but it will have
1722 * XML re-written by our parser. Overwrite the MXF using the given file's verbatim
1725 ASDCP::TimedText::MXFWriter writer;
1726 ASDCP::WriterInfo writer_info;
1727 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
1729 Kumu::hex2bin (asset->id().c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
1730 DCP_ASSERT (c == Kumu::UUID_Length);
1731 ASDCP::TimedText::TimedTextDescriptor descriptor;
1732 descriptor.ContainerDuration = asset->intrinsic_duration();
1733 Kumu::hex2bin (asset->xml_id()->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c);
1734 DCP_ASSERT (c == Kumu::UUID_Length);
1735 ASDCP::Result_t r = writer.OpenWrite (subs_mxf.string().c_str(), writer_info, descriptor, 16384);
1736 BOOST_REQUIRE (!ASDCP_FAILURE(r));
1737 r = writer.WriteTimedTextResource (dcp::file_to_string(subs_xml));
1738 BOOST_REQUIRE (!ASDCP_FAILURE(r));
1741 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
1742 return write_dcp_with_single_asset (dir, reel_asset);
1746 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time)
1748 auto const dir = path("build/test/verify_invalid_subtitle_first_text_time");
1749 /* Just too early */
1750 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1751 check_verify_result (
1754 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1755 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1761 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time)
1763 auto const dir = path("build/test/verify_valid_subtitle_first_text_time");
1764 /* Just late enough */
1765 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1766 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1770 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel)
1772 auto const dir = path("build/test/verify_valid_subtitle_first_text_time_on_second_reel");
1773 prepare_directory (dir);
1775 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1776 asset1->set_start_time (dcp::Time());
1777 /* Just late enough */
1778 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1779 asset1->set_language (dcp::LanguageTag("de-DE"));
1780 asset1->write (dir / "subs1.mxf");
1781 auto reel_asset1 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset1, dcp::Fraction(24, 1), 5 * 24, 0);
1782 auto reel1 = make_shared<dcp::Reel>();
1783 reel1->add (reel_asset1);
1784 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 5 * 24);
1785 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1786 reel1->add (markers1);
1788 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1789 asset2->set_start_time (dcp::Time());
1790 /* This would be too early on first reel but should be OK on the second */
1791 add_test_subtitle (asset2, 3, 4 * 24);
1792 asset2->set_language (dcp::LanguageTag("de-DE"));
1793 asset2->write (dir / "subs2.mxf");
1794 auto reel_asset2 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset2, dcp::Fraction(24, 1), 4 * 24, 0);
1795 auto reel2 = make_shared<dcp::Reel>();
1796 reel2->add (reel_asset2);
1797 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 4 * 24);
1798 markers2->set (dcp::Marker::LFOC, dcp::Time(4 * 24 - 1, 24, 24));
1799 reel2->add (markers2);
1801 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1804 auto dcp = make_shared<dcp::DCP>(dir);
1806 dcp->set_annotation_text("hello");
1809 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1813 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing)
1815 auto const dir = path("build/test/verify_invalid_subtitle_spacing");
1816 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1820 { 5 * 24 + 1, 6 * 24 },
1822 check_verify_result (
1825 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING },
1826 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1831 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_spacing)
1833 auto const dir = path("build/test/verify_valid_subtitle_spacing");
1834 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1838 { 5 * 24 + 16, 8 * 24 },
1840 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1844 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_duration)
1846 auto const dir = path("build/test/verify_invalid_subtitle_duration");
1847 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1848 check_verify_result (
1851 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION },
1852 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1857 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_duration)
1859 auto const dir = path("build/test/verify_valid_subtitle_duration");
1860 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1861 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1865 BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary)
1867 auto const dir = path("build/test/verify_subtitle_overlapping_reel_boundary");
1868 prepare_directory (dir);
1869 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1870 asset->set_start_time (dcp::Time());
1871 add_test_subtitle (asset, 0, 4 * 24);
1872 asset->set_language (dcp::LanguageTag("de-DE"));
1873 asset->write (dir / "subs.mxf");
1875 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 3 * 24, 0);
1876 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1877 check_verify_result (
1880 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get()) },
1881 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1882 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY },
1883 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1889 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count1)
1891 auto const dir = path ("build/test/invalid_subtitle_line_count1");
1892 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1895 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1896 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1897 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1898 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
1900 check_verify_result (
1903 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT },
1904 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1909 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count1)
1911 auto const dir = path ("build/test/verify_valid_subtitle_line_count1");
1912 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1915 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1916 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1917 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1919 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1923 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count2)
1925 auto const dir = path ("build/test/verify_invalid_subtitle_line_count2");
1926 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1929 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
1930 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
1931 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
1932 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
1934 check_verify_result (
1937 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT },
1938 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1943 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count2)
1945 auto const dir = path ("build/test/verify_valid_subtitle_line_count2");
1946 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1949 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
1950 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
1951 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
1952 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
1954 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1958 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length1)
1960 auto const dir = path ("build/test/verify_invalid_subtitle_line_length1");
1961 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1964 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123" }
1966 check_verify_result (
1969 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH },
1970 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1975 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length2)
1977 auto const dir = path ("build/test/verify_invalid_subtitle_line_length2");
1978 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1981 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1983 check_verify_result (
1986 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH },
1987 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1992 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count1)
1994 auto const dir = path ("build/test/verify_valid_closed_caption_line_count1");
1995 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
1998 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1999 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2000 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2001 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
2003 check_verify_result (
2006 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT},
2007 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2012 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count2)
2014 auto const dir = path ("build/test/verify_valid_closed_caption_line_count2");
2015 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2018 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2019 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2020 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2022 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2026 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_count3)
2028 auto const dir = path ("build/test/verify_invalid_closed_caption_line_count3");
2029 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2032 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2033 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2034 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2035 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2037 check_verify_result (
2040 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT},
2041 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2046 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count4)
2048 auto const dir = path ("build/test/verify_valid_closed_caption_line_count4");
2049 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2052 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2053 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2054 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2055 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2057 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2061 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_length)
2063 auto const dir = path ("build/test/verify_valid_closed_caption_line_length");
2064 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2067 { 96, 300, 0.0, dcp::VAlign::CENTER, "01234567890123456789012345678901" }
2069 check_verify_result (
2072 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2077 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length)
2079 auto const dir = path ("build/test/verify_invalid_closed_caption_line_length");
2080 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2083 { 96, 300, 0.0, dcp::VAlign::CENTER, "0123456789012345678901234567890123" }
2085 check_verify_result (
2088 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH },
2089 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2094 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1)
2096 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign1");
2097 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2100 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2101 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2102 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2104 check_verify_result (
2107 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2112 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2)
2114 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign2");
2115 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2118 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2119 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2120 { 96, 300, 0.2, dcp::VAlign::CENTER, "not fine" },
2122 check_verify_result (
2125 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN },
2126 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2131 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1)
2133 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering1");
2134 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2137 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2138 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2139 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2141 check_verify_result (
2144 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2149 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2)
2151 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering2");
2152 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2155 { 96, 300, 0.2, dcp::VAlign::BOTTOM, "This" },
2156 { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" },
2157 { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" },
2159 check_verify_result (
2162 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2167 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3)
2169 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering3");
2170 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering3.xml");
2171 check_verify_result (
2174 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING },
2175 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2180 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4)
2182 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4");
2183 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml");
2184 check_verify_result (
2187 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2193 BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate)
2195 path const dir("build/test/verify_invalid_sound_frame_rate");
2196 prepare_directory (dir);
2198 auto picture = simple_picture (dir, "foo");
2199 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
2200 auto reel = make_shared<dcp::Reel>();
2201 reel->add (reel_picture);
2202 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000, boost::none);
2203 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
2204 reel->add (reel_sound);
2205 reel->add (simple_markers());
2206 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2208 auto dcp = make_shared<dcp::DCP>(dir);
2210 dcp->set_annotation_text("hello");
2213 check_verify_result (
2216 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf") },
2217 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
2222 BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text)
2224 path const dir("build/test/verify_missing_cpl_annotation_text");
2225 auto dcp = make_simple (dir);
2226 dcp->set_annotation_text("A Test DCP");
2229 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2231 auto const cpl = dcp->cpls()[0];
2234 BOOST_REQUIRE (cpl->file());
2235 Editor e(cpl->file().get());
2236 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
2239 check_verify_result (
2242 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) },
2243 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) }
2248 BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text)
2250 path const dir("build/test/verify_mismatched_cpl_annotation_text");
2251 auto dcp = make_simple (dir);
2252 dcp->set_annotation_text("A Test DCP");
2255 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2256 auto const cpl = dcp->cpls()[0];
2259 BOOST_REQUIRE (cpl->file());
2260 Editor e(cpl->file().get());
2261 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
2264 check_verify_result (
2267 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) },
2268 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) }
2273 BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration)
2275 path const dir("build/test/verify_mismatched_asset_duration");
2276 prepare_directory (dir);
2277 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
2278 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2280 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
2281 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
2283 auto reel = make_shared<dcp::Reel>(
2284 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
2285 make_shared<dcp::ReelSoundAsset>(ms, 0)
2288 reel->add (simple_markers());
2292 dcp->set_annotation_text("A Test DCP");
2295 check_verify_result (
2298 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION },
2299 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), canonical(cpl->file().get()) }
2306 shared_ptr<dcp::CPL>
2307 verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool add_to_reel2)
2309 prepare_directory (dir);
2310 auto dcp = make_shared<dcp::DCP>(dir);
2311 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2313 auto constexpr reel_length = 192;
2315 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2316 subs->set_language (dcp::LanguageTag("de-DE"));
2317 subs->set_start_time (dcp::Time());
2318 subs->add (simple_subtitle());
2319 subs->write (dir / "subs.mxf");
2320 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2322 auto reel1 = make_shared<dcp::Reel>(
2323 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2324 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2328 reel1->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2331 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2332 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2333 reel1->add (markers1);
2337 auto reel2 = make_shared<dcp::Reel>(
2338 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2339 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2343 reel2->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2346 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2347 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
2348 reel2->add (markers2);
2353 dcp->set_annotation_text("A Test DCP");
2360 BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels)
2363 path dir ("build/test/missing_main_subtitle_from_some_reels");
2364 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, false);
2365 check_verify_result (
2368 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS },
2369 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
2375 path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
2376 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, true);
2377 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2381 path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
2382 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, false, false);
2383 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2389 shared_ptr<dcp::CPL>
2390 verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, int caps_in_reel2)
2392 prepare_directory (dir);
2393 auto dcp = make_shared<dcp::DCP>(dir);
2394 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2396 auto constexpr reel_length = 192;
2398 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2399 subs->set_language (dcp::LanguageTag("de-DE"));
2400 subs->set_start_time (dcp::Time());
2401 subs->add (simple_subtitle());
2402 subs->write (dir / "subs.mxf");
2404 auto reel1 = make_shared<dcp::Reel>(
2405 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2406 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2409 for (int i = 0; i < caps_in_reel1; ++i) {
2410 reel1->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2413 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2414 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2415 reel1->add (markers1);
2419 auto reel2 = make_shared<dcp::Reel>(
2420 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2421 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2424 for (int i = 0; i < caps_in_reel2; ++i) {
2425 reel2->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2428 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2429 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
2430 reel2->add (markers2);
2435 dcp->set_annotation_text("A Test DCP");
2442 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_asset_counts)
2445 path dir ("build/test/mismatched_closed_caption_asset_counts");
2446 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2447 check_verify_result (
2450 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS },
2451 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2456 path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2457 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2458 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2462 path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2463 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2464 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2471 verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2473 prepare_directory (dir);
2474 auto dcp = make_shared<dcp::DCP>(dir);
2475 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2477 auto constexpr reel_length = 192;
2479 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2480 subs->set_language (dcp::LanguageTag("de-DE"));
2481 subs->set_start_time (dcp::Time());
2482 subs->add (simple_subtitle());
2483 subs->write (dir / "subs.mxf");
2484 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), reel_length, 0);
2487 auto reel = make_shared<dcp::Reel>(
2488 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2489 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2492 reel->add (reel_text);
2494 reel->add (simple_markers(reel_length));
2499 dcp->set_annotation_text("A Test DCP");
2502 check_verify_result (
2505 { dcp::VerificationNote::Type::BV21_ERROR, code, subs->id() },
2506 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
2511 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2513 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
2514 "build/test/verify_subtitle_entry_point_must_be_present",
2515 dcp::VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT,
2516 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
2517 asset->unset_entry_point ();
2521 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
2522 "build/test/verify_subtitle_entry_point_must_be_zero",
2523 dcp::VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT,
2524 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
2525 asset->set_entry_point (4);
2529 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
2530 "build/test/verify_closed_caption_entry_point_must_be_present",
2531 dcp::VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2532 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
2533 asset->unset_entry_point ();
2537 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
2538 "build/test/verify_closed_caption_entry_point_must_be_zero",
2539 dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT,
2540 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
2541 asset->set_entry_point (9);
2547 BOOST_AUTO_TEST_CASE (verify_missing_hash)
2551 path const dir("build/test/verify_missing_hash");
2552 auto dcp = make_simple (dir);
2553 dcp->set_annotation_text("A Test DCP");
2556 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2557 auto const cpl = dcp->cpls()[0];
2558 BOOST_REQUIRE_EQUAL (cpl->reels().size(), 1U);
2559 BOOST_REQUIRE (cpl->reels()[0]->main_picture());
2560 auto asset_id = cpl->reels()[0]->main_picture()->id();
2563 BOOST_REQUIRE (cpl->file());
2564 Editor e(cpl->file().get());
2565 e.delete_first_line_containing("<Hash>");
2568 check_verify_result (
2571 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2572 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id }
2579 verify_markers_test (
2581 vector<pair<dcp::Marker, dcp::Time>> markers,
2582 vector<dcp::VerificationNote> test_notes
2585 auto dcp = make_simple (dir);
2586 dcp->cpls()[0]->set_content_kind (dcp::ContentKind::FEATURE);
2587 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24);
2588 for (auto const& i: markers) {
2589 markers_asset->set (i.first, i.second);
2591 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2592 dcp->set_annotation_text("A Test DCP");
2595 check_verify_result ({dir}, test_notes);
2599 BOOST_AUTO_TEST_CASE (verify_markers)
2601 verify_markers_test (
2602 "build/test/verify_markers_all_correct",
2604 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2605 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2606 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2607 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2612 verify_markers_test (
2613 "build/test/verify_markers_missing_ffec",
2615 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2616 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2617 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2620 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }
2623 verify_markers_test (
2624 "build/test/verify_markers_missing_ffmc",
2626 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2627 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2628 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2631 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }
2634 verify_markers_test (
2635 "build/test/verify_markers_missing_ffoc",
2637 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2638 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2639 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2642 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC}
2645 verify_markers_test (
2646 "build/test/verify_markers_missing_lfoc",
2648 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2649 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2650 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2653 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }
2656 verify_markers_test (
2657 "build/test/verify_markers_incorrect_ffoc",
2659 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2660 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2661 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2662 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2665 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_FFOC, string("3") }
2668 verify_markers_test (
2669 "build/test/verify_markers_incorrect_lfoc",
2671 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2672 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2673 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2674 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2677 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_LFOC, string("18") }
2682 BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number)
2684 path dir = "build/test/verify_missing_cpl_metadata_version_number";
2685 prepare_directory (dir);
2686 auto dcp = make_simple (dir);
2687 auto cpl = dcp->cpls()[0];
2688 cpl->unset_version_number();
2689 dcp->set_annotation_text("A Test DCP");
2692 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get() }});
2696 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1)
2698 path dir = "build/test/verify_missing_extension_metadata1";
2699 auto dcp = make_simple (dir);
2700 dcp->set_annotation_text("A Test DCP");
2703 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2704 auto cpl = dcp->cpls()[0];
2707 Editor e (cpl->file().get());
2708 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2711 check_verify_result (
2714 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2715 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() }
2720 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2)
2722 path dir = "build/test/verify_missing_extension_metadata2";
2723 auto dcp = make_simple (dir);
2724 dcp->set_annotation_text("A Test DCP");
2727 auto cpl = dcp->cpls()[0];
2730 Editor e (cpl->file().get());
2731 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2734 check_verify_result (
2737 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2738 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() }
2743 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3)
2745 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata3";
2746 auto dcp = make_simple (dir);
2747 dcp->set_annotation_text("A Test DCP");
2750 auto const cpl = dcp->cpls()[0];
2753 Editor e (cpl->file().get());
2754 e.replace ("<meta:Name>A", "<meta:NameX>A");
2755 e.replace ("n</meta:Name>", "n</meta:NameX>");
2758 check_verify_result (
2761 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70 },
2762 { 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 },
2763 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2768 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1)
2770 path dir = "build/test/verify_invalid_extension_metadata1";
2771 auto dcp = make_simple (dir);
2772 dcp->set_annotation_text("A Test DCP");
2775 auto cpl = dcp->cpls()[0];
2778 Editor e (cpl->file().get());
2779 e.replace ("Application", "Fred");
2782 check_verify_result (
2785 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2786 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get() },
2791 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2)
2793 path dir = "build/test/verify_invalid_extension_metadata2";
2794 auto dcp = make_simple (dir);
2795 dcp->set_annotation_text("A Test DCP");
2798 auto cpl = dcp->cpls()[0];
2801 Editor e (cpl->file().get());
2802 e.replace ("DCP Constraints Profile", "Fred");
2805 check_verify_result (
2808 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2809 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get() },
2814 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6)
2816 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata6";
2817 auto dcp = make_simple (dir);
2818 dcp->set_annotation_text("A Test DCP");
2821 auto const cpl = dcp->cpls()[0];
2824 Editor e (cpl->file().get());
2825 e.replace ("<meta:Value>", "<meta:ValueX>");
2826 e.replace ("</meta:Value>", "</meta:ValueX>");
2829 check_verify_result (
2832 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74 },
2833 { 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 },
2834 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2839 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7)
2841 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata7";
2842 auto dcp = make_simple (dir);
2843 dcp->set_annotation_text("A Test DCP");
2846 auto const cpl = dcp->cpls()[0];
2849 Editor e (cpl->file().get());
2850 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2853 check_verify_result (
2856 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2857 { 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() },
2862 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8)
2864 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata8";
2865 auto dcp = make_simple (dir);
2866 dcp->set_annotation_text("A Test DCP");
2869 auto const cpl = dcp->cpls()[0];
2872 Editor e (cpl->file().get());
2873 e.replace ("<meta:Property>", "<meta:PropertyX>");
2874 e.replace ("</meta:Property>", "</meta:PropertyX>");
2877 check_verify_result (
2880 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72 },
2881 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyX' is not allowed for content model '(Property+)'"), cpl->file().get(), 76 },
2882 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2887 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9)
2889 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata9";
2890 auto dcp = make_simple (dir);
2891 dcp->set_annotation_text("A Test DCP");
2894 auto const cpl = dcp->cpls()[0];
2897 Editor e (cpl->file().get());
2898 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2899 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2902 check_verify_result (
2905 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71 },
2906 { 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 },
2907 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2913 BOOST_AUTO_TEST_CASE (verify_unsigned_cpl_with_encrypted_content)
2915 path dir = "build/test/verify_unsigned_cpl_with_encrypted_content";
2916 prepare_directory (dir);
2917 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
2918 copy_file (i.path(), dir / i.path().filename());
2921 path const pkl = dir / ( "pkl_" + encryption_test_pkl_id + ".xml" );
2922 path const cpl = dir / ( "cpl_" + encryption_test_cpl_id + ".xml");
2926 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2929 check_verify_result (
2932 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, encryption_test_cpl_id, canonical(cpl) },
2933 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id, canonical(pkl), },
2934 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE },
2935 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE },
2936 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC },
2937 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC },
2938 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id, canonical(cpl) },
2939 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, encryption_test_cpl_id, canonical(cpl) }
2944 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_encrypted_content)
2946 path dir = "build/test/unsigned_pkl_with_encrypted_content";
2947 prepare_directory (dir);
2948 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
2949 copy_file (i.path(), dir / i.path().filename());
2952 path const cpl = dir / ("cpl_" + encryption_test_cpl_id + ".xml");
2953 path const pkl = dir / ("pkl_" + encryption_test_pkl_id + ".xml");
2956 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2959 check_verify_result (
2962 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id, canonical(pkl) },
2963 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE },
2964 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE },
2965 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC },
2966 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC },
2967 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id, canonical(cpl) },
2968 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id, canonical(pkl) },
2973 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_unencrypted_content)
2975 path dir = "build/test/verify_unsigned_pkl_with_unencrypted_content";
2976 prepare_directory (dir);
2977 for (auto i: directory_iterator("test/ref/DCP/dcp_test1")) {
2978 copy_file (i.path(), dir / i.path().filename());
2982 Editor e (dir / dcp_test1_pkl);
2983 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2986 check_verify_result ({dir}, {});
2990 BOOST_AUTO_TEST_CASE (verify_partially_encrypted)
2992 path dir ("build/test/verify_must_not_be_partially_encrypted");
2993 prepare_directory (dir);
2997 auto signer = make_shared<dcp::CertificateChain>();
2998 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
2999 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
3000 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
3001 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
3003 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3007 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
3010 auto writer = mp->start_write (dir / "video.mxf", false);
3011 dcp::ArrayData j2c ("test/data/flat_red.j2c");
3012 for (int i = 0; i < 24; ++i) {
3013 writer->write (j2c.data(), j2c.size());
3015 writer->finalize ();
3017 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
3019 auto reel = make_shared<dcp::Reel>(
3020 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
3021 make_shared<dcp::ReelSoundAsset>(ms, 0)
3024 reel->add (simple_markers());
3028 cpl->set_content_version (
3029 {"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"}
3031 cpl->set_annotation_text ("A Test DCP");
3032 cpl->set_issuer ("OpenDCP 0.0.25");
3033 cpl->set_creator ("OpenDCP 0.0.25");
3034 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
3035 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
3036 cpl->set_main_sound_sample_rate (48000);
3037 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
3038 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
3039 cpl->set_version_number (1);
3043 d.set_issuer("OpenDCP 0.0.25");
3044 d.set_creator("OpenDCP 0.0.25");
3045 d.set_issue_date("2012-07-17T04:45:18+00:00");
3046 d.set_annotation_text("A Test DCP");
3047 d.write_xml(signer);
3049 check_verify_result (
3052 {dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED},
3057 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k)
3059 vector<dcp::VerificationNote> notes;
3060 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"));
3061 auto reader = picture.start_read ();
3062 auto frame = reader->get_frame (0);
3063 verify_j2k (frame, notes);
3064 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3068 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k)
3070 vector<dcp::VerificationNote> notes;
3071 dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR"));
3072 auto reader = picture.start_read ();
3073 auto frame = reader->get_frame (0);
3074 verify_j2k (frame, notes);
3075 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3079 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp)
3081 boost::filesystem::path dir = "build/test/verify_jpeg2000_codestream_libdcp";
3082 prepare_directory (dir);
3083 auto dcp = make_simple (dir);
3085 vector<dcp::VerificationNote> notes;
3086 dcp::MonoPictureAsset picture (find_file(dir, "video"));
3087 auto reader = picture.start_read ();
3088 auto frame = reader->get_frame (0);
3089 verify_j2k (frame, notes);
3090 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3094 /** Check that ResourceID and the XML ID being different is spotted */
3095 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id)
3097 boost::filesystem::path const dir = "build/test/verify_mismatched_subtitle_resource_id";
3098 prepare_directory (dir);
3100 ASDCP::WriterInfo writer_info;
3101 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
3104 auto mxf_id = dcp::make_uuid ();
3105 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
3106 BOOST_REQUIRE (c == Kumu::UUID_Length);
3108 auto resource_id = dcp::make_uuid ();
3109 ASDCP::TimedText::TimedTextDescriptor descriptor;
3110 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
3111 DCP_ASSERT (c == Kumu::UUID_Length);
3113 auto xml_id = dcp::make_uuid ();
3114 ASDCP::TimedText::MXFWriter writer;
3115 auto subs_mxf = dir / "subs.mxf";
3116 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
3117 BOOST_REQUIRE (ASDCP_SUCCESS(r));
3118 writer.WriteTimedTextResource (dcp::String::compose(
3119 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
3120 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
3121 "<Id>urn:uuid:%1</Id>"
3122 "<ContentTitleText>Content</ContentTitleText>"
3123 "<AnnotationText>Annotation</AnnotationText>"
3124 "<IssueDate>2018-10-02T12:25:14</IssueDate>"
3125 "<ReelNumber>1</ReelNumber>"
3126 "<Language>en-US</Language>"
3127 "<EditRate>25 1</EditRate>"
3128 "<TimeCodeRate>25</TimeCodeRate>"
3129 "<StartTime>00:00:00:00</StartTime>"
3131 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
3132 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
3133 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
3142 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
3143 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
3145 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
3147 check_verify_result (
3150 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) },
3151 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID },
3152 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
3153 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
3158 /** Check that ResourceID and the MXF ID being the same is spotted */
3159 BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id)
3161 boost::filesystem::path const dir = "build/test/verify_incorrect_timed_text_id";
3162 prepare_directory (dir);
3164 ASDCP::WriterInfo writer_info;
3165 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
3168 auto mxf_id = dcp::make_uuid ();
3169 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
3170 BOOST_REQUIRE (c == Kumu::UUID_Length);
3172 auto resource_id = mxf_id;
3173 ASDCP::TimedText::TimedTextDescriptor descriptor;
3174 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
3175 DCP_ASSERT (c == Kumu::UUID_Length);
3177 auto xml_id = resource_id;
3178 ASDCP::TimedText::MXFWriter writer;
3179 auto subs_mxf = dir / "subs.mxf";
3180 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
3181 BOOST_REQUIRE (ASDCP_SUCCESS(r));
3182 writer.WriteTimedTextResource (dcp::String::compose(
3183 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
3184 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
3185 "<Id>urn:uuid:%1</Id>"
3186 "<ContentTitleText>Content</ContentTitleText>"
3187 "<AnnotationText>Annotation</AnnotationText>"
3188 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
3189 "<ReelNumber>1</ReelNumber>"
3190 "<Language>en-US</Language>"
3191 "<EditRate>25 1</EditRate>"
3192 "<TimeCodeRate>25</TimeCodeRate>"
3193 "<StartTime>00:00:00:00</StartTime>"
3195 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
3196 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
3197 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
3206 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
3207 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
3209 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
3211 check_verify_result (
3214 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) },
3215 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID },
3216 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
3217 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
3218 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_ISSUE_DATE, string{"2018-10-02T12:25:14+02:00"} }
3223 /** Check a DCP with a 3D asset marked as 2D */
3224 BOOST_AUTO_TEST_CASE (verify_threed_marked_as_twod)
3226 check_verify_result (
3227 { private_test / "data" / "xm" },
3230 dcp::VerificationNote::Type::WARNING,
3231 dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(private_test / "data" / "xm", "j2c"))
3234 dcp::VerificationNote::Type::BV21_ERROR,
3235 dcp::VerificationNote::Code::INVALID_STANDARD
3242 BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers)
3244 path dir = "build/test/verify_unexpected_things_in_main_markers";
3245 prepare_directory (dir);
3246 auto dcp = make_simple (dir, 1, 24);
3247 dcp->set_annotation_text("A Test DCP");
3251 Editor e (find_cpl(dir));
3253 " <IntrinsicDuration>24</IntrinsicDuration>",
3254 "<EntryPoint>0</EntryPoint><Duration>24</Duration>"
3258 dcp::CPL cpl (find_cpl(dir));
3260 check_verify_result (
3263 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3264 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT },
3265 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION },
3270 BOOST_AUTO_TEST_CASE(verify_invalid_content_kind)
3272 path dir = "build/test/verify_invalid_content_kind";
3273 prepare_directory (dir);
3274 auto dcp = make_simple (dir, 1, 24);
3275 dcp->set_annotation_text("A Test DCP");
3279 Editor e(find_cpl(dir));
3280 e.replace("trailer", "trip");
3283 dcp::CPL cpl (find_cpl(dir));
3285 check_verify_result (
3288 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3289 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip") }
3295 BOOST_AUTO_TEST_CASE(verify_valid_content_kind)
3297 path dir = "build/test/verify_valid_content_kind";
3298 prepare_directory (dir);
3299 auto dcp = make_simple (dir, 1, 24);
3300 dcp->set_annotation_text("A Test DCP");
3304 Editor e(find_cpl(dir));
3305 e.replace("<ContentKind>trailer</ContentKind>", "<ContentKind scope=\"http://bobs.contents/\">trip</ContentKind>");
3308 dcp::CPL cpl (find_cpl(dir));
3310 check_verify_result (
3313 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3319 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1)
3321 path dir = "build/test/verify_invalid_main_picture_active_area_1";
3322 prepare_directory(dir);
3323 auto dcp = make_simple(dir, 1, 24);
3326 auto constexpr area = "<meta:MainPictureActiveArea>";
3329 Editor e(find_cpl(dir));
3330 e.delete_lines_after(area, 2);
3331 e.insert(area, "<meta:Height>4080</meta:Height>");
3332 e.insert(area, "<meta:Width>1997</meta:Width>");
3335 dcp::PKL pkl(find_pkl(dir));
3336 dcp::CPL cpl(find_cpl(dir));
3338 check_verify_result(
3341 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3342 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3343 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir)) },
3344 { 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)) },
3349 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2)
3351 path dir = "build/test/verify_invalid_main_picture_active_area_2";
3352 prepare_directory(dir);
3353 auto dcp = make_simple(dir, 1, 24);
3356 auto constexpr area = "<meta:MainPictureActiveArea>";
3359 Editor e(find_cpl(dir));
3360 e.delete_lines_after(area, 2);
3361 e.insert(area, "<meta:Height>5125</meta:Height>");
3362 e.insert(area, "<meta:Width>9900</meta:Width>");
3365 dcp::PKL pkl(find_pkl(dir));
3366 dcp::CPL cpl(find_cpl(dir));
3368 check_verify_result(
3371 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3372 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3373 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir)) },
3374 { 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)) },
3375 { 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)) },
3380 BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids)
3384 path dir = "build/test/verify_duplicate_pkl_asset_ids";
3385 prepare_directory(dir);
3386 auto dcp = make_simple(dir, 1, 24);
3390 Editor e(find_pkl(dir));
3391 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab");
3394 dcp::PKL pkl(find_pkl(dir));
3396 check_verify_result(
3399 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) },
3404 BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids)
3408 path dir = "build/test/verify_duplicate_assetmap_asset_ids";
3409 prepare_directory(dir);
3410 auto dcp = make_simple(dir, 1, 24);
3414 Editor e(find_asset_map(dir));
3415 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa");
3418 dcp::PKL pkl(find_pkl(dir));
3419 dcp::AssetMap asset_map(find_asset_map(dir));
3421 check_verify_result(
3424 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3425 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir)) },
3426 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54") },