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);
837 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
841 BOOST_AUTO_TEST_CASE (verify_invalid_smpte_subtitles)
843 using namespace boost::filesystem;
845 path const dir("build/test/verify_invalid_smpte_subtitles");
846 prepare_directory (dir);
847 /* This broken_smpte.mxf does not use urn:uuid: for its subtitle ID, which we tolerate (rightly or wrongly) */
848 copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
849 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
850 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
851 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
853 check_verify_result (
856 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'Foo'"), path(), 2 },
858 dcp::VerificationNote::Type::ERROR,
859 dcp::VerificationNote::Code::INVALID_XML,
860 string("element 'Foo' is not allowed for content model '(Id,ContentTitleText,AnnotationText?,IssueDate,ReelNumber?,Language?,EditRate,TimeCodeRate,StartTime?,DisplayType?,LoadFont*,SubtitleList)'"),
864 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
865 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
870 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles)
872 path const dir("build/test/verify_empty_text_node_in_subtitles");
873 prepare_directory (dir);
874 copy_file ("test/data/empty_text.mxf", dir / "subs.mxf");
875 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
876 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
877 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
879 check_verify_result (
882 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT },
883 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
884 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") },
885 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
890 /** A <Text> node with no content except some <Font> nodes, which themselves do have content */
891 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_child_nodes)
893 path const dir("build/test/verify_empty_text_node_in_subtitles_with_child_nodes");
894 prepare_directory (dir);
895 copy_file ("test/data/empty_but_with_children.xml", dir / "subs.xml");
896 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
897 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
898 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
900 check_verify_result (
903 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
908 /** A <Text> node with no content except some <Font> nodes, which themselves also have no content */
909 BOOST_AUTO_TEST_CASE (verify_empty_text_node_in_subtitles_with_empty_child_nodes)
911 path const dir("build/test/verify_empty_text_node_in_subtitles_with_empty_child_nodes");
912 prepare_directory (dir);
913 copy_file ("test/data/empty_with_empty_children.xml", dir / "subs.xml");
914 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
915 auto reel_asset = make_shared<dcp::ReelInteropSubtitleAsset>(asset, dcp::Fraction(24, 1), 192, 0);
916 auto cpl = write_dcp_with_single_asset (dir, reel_asset, dcp::Standard::INTEROP);
918 check_verify_result (
921 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE, asset->id(), boost::filesystem::canonical(asset->file().get()) },
922 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
923 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT },
928 BOOST_AUTO_TEST_CASE (verify_external_asset)
930 path const ov_dir("build/test/verify_external_asset");
931 prepare_directory (ov_dir);
933 auto image = black_image ();
934 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
935 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
936 dcp_from_frame (frame, ov_dir);
938 dcp::DCP ov (ov_dir);
941 path const vf_dir("build/test/verify_external_asset_vf");
942 prepare_directory (vf_dir);
944 auto picture = ov.cpls()[0]->reels()[0]->main_picture();
945 auto cpl = write_dcp_with_single_asset (vf_dir, picture);
947 check_verify_result (
950 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, picture->asset()->id() },
951 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
956 BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata)
958 path const dir("build/test/verify_valid_cpl_metadata");
959 prepare_directory (dir);
961 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
962 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
963 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
965 auto reel = make_shared<dcp::Reel>();
966 reel->add (reel_asset);
968 reel->add (make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 16 * 24), 0));
969 reel->add (simple_markers(16 * 24));
971 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
973 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
974 cpl->set_main_sound_sample_rate (48000);
975 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
976 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
977 cpl->set_version_number (1);
981 dcp.set_annotation_text("hello");
987 find_prefix(path dir, string prefix)
989 auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) {
990 return boost::starts_with(p.filename().string(), prefix);
993 BOOST_REQUIRE(iter != directory_iterator());
998 path find_cpl (path dir)
1000 return find_prefix(dir, "cpl_");
1007 return find_prefix(dir, "pkl_");
1012 find_asset_map(path dir)
1014 return find_prefix(dir, "ASSETMAP");
1018 /* DCP with invalid CompositionMetadataAsset */
1019 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag)
1021 using namespace boost::filesystem;
1023 path const dir("build/test/verify_invalid_cpl_metadata_bad_tag");
1024 prepare_directory (dir);
1026 auto reel = make_shared<dcp::Reel>();
1027 reel->add (black_picture_asset(dir));
1028 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1030 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1031 cpl->set_main_sound_sample_rate (48000);
1032 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1033 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1034 cpl->set_version_number (1);
1036 reel->add (simple_markers());
1040 dcp.set_annotation_text("hello");
1044 Editor e (find_cpl(dir));
1045 e.replace ("MainSound", "MainSoundX");
1048 check_verify_result (
1051 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50 },
1052 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51 },
1054 dcp::VerificationNote::Type::ERROR,
1055 dcp::VerificationNote::Code::INVALID_XML,
1056 string("element 'meta:MainSoundXConfiguration' is not allowed for content model "
1057 "'(Id,AnnotationText?,EditRate,IntrinsicDuration,EntryPoint?,Duration?,"
1058 "FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
1059 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,"
1060 "MainSoundSampleRate,MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,"
1061 "ExtensionMetadataList?,)'"),
1062 canonical(cpl->file().get()),
1065 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) },
1070 /* DCP with invalid CompositionMetadataAsset */
1071 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_missing_tag)
1073 path const dir("build/test/verify_invalid_cpl_metadata_missing_tag");
1074 prepare_directory (dir);
1076 auto reel = make_shared<dcp::Reel>();
1077 reel->add (black_picture_asset(dir));
1078 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1080 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1081 cpl->set_main_sound_sample_rate (48000);
1082 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1083 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1087 dcp.set_annotation_text("hello");
1091 Editor e (find_cpl(dir));
1092 e.replace ("meta:Width", "meta:WidthX");
1095 check_verify_result (
1097 {{ dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::FAILED_READ, string("missing XML tag Width in MainPictureStoredArea") }}
1102 BOOST_AUTO_TEST_CASE (verify_invalid_language1)
1104 path const dir("build/test/verify_invalid_language1");
1105 prepare_directory (dir);
1106 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1107 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1108 asset->_language = "wrong-andbad";
1109 asset->write (dir / "subs.mxf");
1110 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1111 reel_asset->_language = "badlang";
1112 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1114 check_verify_result (
1117 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") },
1118 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") },
1119 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1124 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
1125 BOOST_AUTO_TEST_CASE (verify_invalid_language2)
1127 path const dir("build/test/verify_invalid_language2");
1128 prepare_directory (dir);
1129 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1130 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1131 asset->_language = "wrong-andbad";
1132 asset->write (dir / "subs.mxf");
1133 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1134 reel_asset->_language = "badlang";
1135 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1137 check_verify_result (
1140 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") },
1141 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") },
1142 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1147 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
1148 * the release territory.
1150 BOOST_AUTO_TEST_CASE (verify_invalid_language3)
1152 path const dir("build/test/verify_invalid_language3");
1153 prepare_directory (dir);
1155 auto picture = simple_picture (dir, "foo");
1156 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1157 auto reel = make_shared<dcp::Reel>();
1158 reel->add (reel_picture);
1159 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
1160 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1161 reel->add (reel_sound);
1162 reel->add (simple_markers());
1164 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1166 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
1167 cpl->_additional_subtitle_languages.push_back("andso-is-this");
1168 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1169 cpl->set_main_sound_sample_rate (48000);
1170 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1171 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1172 cpl->set_version_number (1);
1173 cpl->_release_territory = "fred-jim";
1174 auto dcp = make_shared<dcp::DCP>(dir);
1176 dcp->set_annotation_text("hello");
1179 check_verify_result (
1182 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong") },
1183 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this") },
1184 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim") },
1185 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz") },
1191 vector<dcp::VerificationNote>
1192 check_picture_size (int width, int height, int frame_rate, bool three_d)
1194 using namespace boost::filesystem;
1196 path dcp_path = "build/test/verify_picture_test";
1197 prepare_directory (dcp_path);
1199 shared_ptr<dcp::PictureAsset> mp;
1201 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1203 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1205 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
1207 auto image = black_image (dcp::Size(width, height));
1208 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1209 int const length = three_d ? frame_rate * 2 : frame_rate;
1210 for (int i = 0; i < length; ++i) {
1211 picture_writer->write (j2c.data(), j2c.size());
1213 picture_writer->finalize ();
1215 auto d = make_shared<dcp::DCP>(dcp_path);
1216 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1217 cpl->set_annotation_text ("A Test DCP");
1218 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1219 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1220 cpl->set_main_sound_sample_rate (48000);
1221 cpl->set_main_picture_stored_area(dcp::Size(width, height));
1222 cpl->set_main_picture_active_area(dcp::Size(width, height));
1223 cpl->set_version_number (1);
1225 auto reel = make_shared<dcp::Reel>();
1228 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1230 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1233 reel->add (simple_markers(frame_rate));
1238 d->set_annotation_text("A Test DCP");
1241 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1247 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1249 auto notes = check_picture_size(width, height, frame_rate, three_d);
1250 BOOST_CHECK_EQUAL (notes.size(), 0U);
1256 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1258 auto notes = check_picture_size(width, height, frame_rate, three_d);
1259 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1260 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1261 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS);
1267 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1269 auto notes = check_picture_size(width, height, frame_rate, three_d);
1270 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1271 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::Type::BV21_ERROR);
1272 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K);
1278 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1280 auto notes = check_picture_size(width, height, frame_rate, three_d);
1281 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1282 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1283 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K);
1287 BOOST_AUTO_TEST_CASE (verify_picture_size)
1289 using namespace boost::filesystem;
1292 check_picture_size_ok (2048, 858, 24, false);
1293 check_picture_size_ok (2048, 858, 25, false);
1294 check_picture_size_ok (2048, 858, 48, false);
1295 check_picture_size_ok (2048, 858, 24, true);
1296 check_picture_size_ok (2048, 858, 25, true);
1297 check_picture_size_ok (2048, 858, 48, true);
1300 check_picture_size_ok (1998, 1080, 24, false);
1301 check_picture_size_ok (1998, 1080, 25, false);
1302 check_picture_size_ok (1998, 1080, 48, false);
1303 check_picture_size_ok (1998, 1080, 24, true);
1304 check_picture_size_ok (1998, 1080, 25, true);
1305 check_picture_size_ok (1998, 1080, 48, true);
1308 check_picture_size_ok (4096, 1716, 24, false);
1311 check_picture_size_ok (3996, 2160, 24, false);
1313 /* Bad frame size */
1314 check_picture_size_bad_frame_size (2050, 858, 24, false);
1315 check_picture_size_bad_frame_size (2048, 658, 25, false);
1316 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1317 check_picture_size_bad_frame_size (4000, 2000, 24, true);
1319 /* Bad 2K frame rate */
1320 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1321 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1322 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1324 /* Bad 4K frame rate */
1325 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1326 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1329 auto notes = check_picture_size(3996, 2160, 24, true);
1330 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1331 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1332 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D);
1338 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")
1341 std::make_shared<dcp::SubtitleString>(
1349 dcp::Time(start_frame, 24, 24),
1350 dcp::Time(end_frame, 24, 24),
1352 dcp::HAlign::CENTER,
1356 dcp::Direction::LTR,
1368 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes)
1370 path const dir("build/test/verify_invalid_closed_caption_xml_size_in_bytes");
1371 prepare_directory (dir);
1373 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1374 for (int i = 0; i < 2048; ++i) {
1375 add_test_subtitle (asset, i * 24, i * 24 + 20);
1377 asset->set_language (dcp::LanguageTag("de-DE"));
1378 asset->write (dir / "subs.mxf");
1379 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 49148, 0);
1380 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1382 check_verify_result (
1385 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1387 dcp::VerificationNote::Type::BV21_ERROR,
1388 dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES,
1390 canonical(dir / "subs.mxf")
1392 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1393 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1399 shared_ptr<dcp::SMPTESubtitleAsset>
1400 make_large_subtitle_asset (path font_file)
1402 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1403 dcp::ArrayData big_fake_font(1024 * 1024);
1404 big_fake_font.write (font_file);
1405 for (int i = 0; i < 116; ++i) {
1406 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1414 verify_timed_text_asset_too_large (string name)
1416 auto const dir = path("build/test") / name;
1417 prepare_directory (dir);
1418 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1419 add_test_subtitle (asset, 0, 240);
1420 asset->set_language (dcp::LanguageTag("de-DE"));
1421 asset->write (dir / "subs.mxf");
1423 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 240, 0);
1424 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1426 check_verify_result (
1429 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121695542"), canonical(dir / "subs.mxf") },
1430 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, string("121634816"), canonical(dir / "subs.mxf") },
1431 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1432 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1433 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1438 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1440 verify_timed_text_asset_too_large<dcp::ReelSMPTESubtitleAsset>("verify_subtitle_asset_too_large");
1441 verify_timed_text_asset_too_large<dcp::ReelSMPTEClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1445 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language)
1447 path dir = "build/test/verify_missing_subtitle_language";
1448 prepare_directory (dir);
1449 auto dcp = make_simple (dir, 1, 106);
1452 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1453 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1454 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1455 "<ContentTitleText>Content</ContentTitleText>"
1456 "<AnnotationText>Annotation</AnnotationText>"
1457 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1458 "<ReelNumber>1</ReelNumber>"
1459 "<EditRate>24 1</EditRate>"
1460 "<TimeCodeRate>24</TimeCodeRate>"
1461 "<StartTime>00:00:00:00</StartTime>"
1462 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1464 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1465 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1466 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1472 dcp::File xml_file(dir / "subs.xml", "w");
1473 BOOST_REQUIRE (xml_file);
1474 xml_file.write(xml.c_str(), xml.size(), 1);
1476 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1477 subs->write (dir / "subs.mxf");
1479 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1480 dcp->cpls()[0]->reels()[0]->add(reel_subs);
1481 dcp->set_annotation_text("A Test DCP");
1484 check_verify_result (
1487 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") },
1488 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1493 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages)
1495 path path ("build/test/verify_mismatched_subtitle_languages");
1496 auto constexpr reel_length = 192;
1497 auto dcp = make_simple (path, 2, reel_length);
1498 auto cpl = dcp->cpls()[0];
1501 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1502 subs->set_language (dcp::LanguageTag("de-DE"));
1503 subs->add (simple_subtitle());
1504 subs->write (path / "subs1.mxf");
1505 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
1506 cpl->reels()[0]->add(reel_subs);
1510 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1511 subs->set_language (dcp::LanguageTag("en-US"));
1512 subs->add (simple_subtitle());
1513 subs->write (path / "subs2.mxf");
1514 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
1515 cpl->reels()[1]->add(reel_subs);
1518 dcp->set_annotation_text("A Test DCP");
1521 check_verify_result (
1524 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") },
1525 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") },
1526 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES }
1531 BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed)
1533 path path ("build/test/verify_multiple_closed_caption_languages_allowed");
1534 auto constexpr reel_length = 192;
1535 auto dcp = make_simple (path, 2, reel_length);
1536 auto cpl = dcp->cpls()[0];
1539 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
1540 ccaps->set_language (dcp::LanguageTag("de-DE"));
1541 ccaps->add (simple_subtitle());
1542 ccaps->write (path / "subs1.mxf");
1543 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
1544 cpl->reels()[0]->add(reel_ccaps);
1548 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
1549 ccaps->set_language (dcp::LanguageTag("en-US"));
1550 ccaps->add (simple_subtitle());
1551 ccaps->write (path / "subs2.mxf");
1552 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
1553 cpl->reels()[1]->add(reel_ccaps);
1556 dcp->set_annotation_text("A Test DCP");
1559 check_verify_result (
1562 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") },
1563 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") }
1568 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time)
1570 path dir = "build/test/verify_missing_subtitle_start_time";
1571 prepare_directory (dir);
1572 auto dcp = make_simple (dir, 1, 106);
1575 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1576 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1577 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1578 "<ContentTitleText>Content</ContentTitleText>"
1579 "<AnnotationText>Annotation</AnnotationText>"
1580 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1581 "<ReelNumber>1</ReelNumber>"
1582 "<Language>de-DE</Language>"
1583 "<EditRate>24 1</EditRate>"
1584 "<TimeCodeRate>24</TimeCodeRate>"
1585 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1587 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1588 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1589 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1595 dcp::File xml_file(dir / "subs.xml", "w");
1596 BOOST_REQUIRE (xml_file);
1597 xml_file.write(xml.c_str(), xml.size(), 1);
1599 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1600 subs->write (dir / "subs.mxf");
1602 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1603 dcp->cpls()[0]->reels()[0]->add(reel_subs);
1604 dcp->set_annotation_text("A Test DCP");
1607 check_verify_result (
1610 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1611 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1616 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time)
1618 path dir = "build/test/verify_invalid_subtitle_start_time";
1619 prepare_directory (dir);
1620 auto dcp = make_simple (dir, 1, 106);
1623 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1624 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1625 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1626 "<ContentTitleText>Content</ContentTitleText>"
1627 "<AnnotationText>Annotation</AnnotationText>"
1628 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1629 "<ReelNumber>1</ReelNumber>"
1630 "<Language>de-DE</Language>"
1631 "<EditRate>24 1</EditRate>"
1632 "<TimeCodeRate>24</TimeCodeRate>"
1633 "<StartTime>00:00:02:00</StartTime>"
1634 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1636 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1637 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1638 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1644 dcp::File xml_file(dir / "subs.xml", "w");
1645 BOOST_REQUIRE (xml_file);
1646 xml_file.write(xml.c_str(), xml.size(), 1);
1648 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1649 subs->write (dir / "subs.mxf");
1651 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1652 dcp->cpls().front()->reels().front()->add(reel_subs);
1653 dcp->set_annotation_text("A Test DCP");
1656 check_verify_result (
1659 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1660 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1668 TestText (int in_, int out_, float v_position_ = 0, dcp::VAlign v_align_ = dcp::VAlign::CENTER, string text_ = "Hello")
1671 , v_position(v_position_)
1679 dcp::VAlign v_align;
1685 shared_ptr<dcp::CPL>
1686 dcp_with_text (path dir, vector<TestText> subs)
1688 prepare_directory (dir);
1689 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1690 asset->set_start_time (dcp::Time());
1691 for (auto i: subs) {
1692 add_test_subtitle (asset, i.in, i.out, i.v_position, i.v_align, i.text);
1694 asset->set_language (dcp::LanguageTag("de-DE"));
1695 asset->write (dir / "subs.mxf");
1697 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
1698 return write_dcp_with_single_asset (dir, reel_asset);
1703 shared_ptr<dcp::CPL>
1704 dcp_with_text_from_file (path dir, boost::filesystem::path subs_xml)
1706 prepare_directory (dir);
1707 auto asset = make_shared<dcp::SMPTESubtitleAsset>(subs_xml);
1708 asset->set_start_time (dcp::Time());
1709 asset->set_language (dcp::LanguageTag("de-DE"));
1711 auto subs_mxf = dir / "subs.mxf";
1712 asset->write (subs_mxf);
1714 /* The call to write() puts the asset into the DCP correctly but it will have
1715 * XML re-written by our parser. Overwrite the MXF using the given file's verbatim
1718 ASDCP::TimedText::MXFWriter writer;
1719 ASDCP::WriterInfo writer_info;
1720 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
1722 Kumu::hex2bin (asset->id().c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
1723 DCP_ASSERT (c == Kumu::UUID_Length);
1724 ASDCP::TimedText::TimedTextDescriptor descriptor;
1725 descriptor.ContainerDuration = asset->intrinsic_duration();
1726 Kumu::hex2bin (asset->xml_id()->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c);
1727 DCP_ASSERT (c == Kumu::UUID_Length);
1728 ASDCP::Result_t r = writer.OpenWrite (subs_mxf.string().c_str(), writer_info, descriptor, 16384);
1729 BOOST_REQUIRE (!ASDCP_FAILURE(r));
1730 r = writer.WriteTimedTextResource (dcp::file_to_string(subs_xml));
1731 BOOST_REQUIRE (!ASDCP_FAILURE(r));
1734 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
1735 return write_dcp_with_single_asset (dir, reel_asset);
1739 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time)
1741 auto const dir = path("build/test/verify_invalid_subtitle_first_text_time");
1742 /* Just too early */
1743 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1744 check_verify_result (
1747 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1748 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1754 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time)
1756 auto const dir = path("build/test/verify_valid_subtitle_first_text_time");
1757 /* Just late enough */
1758 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1759 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1763 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel)
1765 auto const dir = path("build/test/verify_valid_subtitle_first_text_time_on_second_reel");
1766 prepare_directory (dir);
1768 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1769 asset1->set_start_time (dcp::Time());
1770 /* Just late enough */
1771 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1772 asset1->set_language (dcp::LanguageTag("de-DE"));
1773 asset1->write (dir / "subs1.mxf");
1774 auto reel_asset1 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset1, dcp::Fraction(24, 1), 5 * 24, 0);
1775 auto reel1 = make_shared<dcp::Reel>();
1776 reel1->add (reel_asset1);
1777 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 5 * 24);
1778 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1779 reel1->add (markers1);
1781 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1782 asset2->set_start_time (dcp::Time());
1783 /* This would be too early on first reel but should be OK on the second */
1784 add_test_subtitle (asset2, 3, 4 * 24);
1785 asset2->set_language (dcp::LanguageTag("de-DE"));
1786 asset2->write (dir / "subs2.mxf");
1787 auto reel_asset2 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset2, dcp::Fraction(24, 1), 4 * 24, 0);
1788 auto reel2 = make_shared<dcp::Reel>();
1789 reel2->add (reel_asset2);
1790 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 4 * 24);
1791 markers2->set (dcp::Marker::LFOC, dcp::Time(4 * 24 - 1, 24, 24));
1792 reel2->add (markers2);
1794 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1797 auto dcp = make_shared<dcp::DCP>(dir);
1799 dcp->set_annotation_text("hello");
1802 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1806 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing)
1808 auto const dir = path("build/test/verify_invalid_subtitle_spacing");
1809 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1813 { 5 * 24 + 1, 6 * 24 },
1815 check_verify_result (
1818 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING },
1819 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1824 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_spacing)
1826 auto const dir = path("build/test/verify_valid_subtitle_spacing");
1827 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1831 { 5 * 24 + 16, 8 * 24 },
1833 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1837 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_duration)
1839 auto const dir = path("build/test/verify_invalid_subtitle_duration");
1840 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1841 check_verify_result (
1844 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION },
1845 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1850 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_duration)
1852 auto const dir = path("build/test/verify_valid_subtitle_duration");
1853 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1854 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1858 BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary)
1860 auto const dir = path("build/test/verify_subtitle_overlapping_reel_boundary");
1861 prepare_directory (dir);
1862 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1863 asset->set_start_time (dcp::Time());
1864 add_test_subtitle (asset, 0, 4 * 24);
1865 asset->set_language (dcp::LanguageTag("de-DE"));
1866 asset->write (dir / "subs.mxf");
1868 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 3 * 24, 0);
1869 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1870 check_verify_result (
1873 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get()) },
1874 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1875 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY },
1876 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1882 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count1)
1884 auto const dir = path ("build/test/invalid_subtitle_line_count1");
1885 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1888 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1889 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1890 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1891 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
1893 check_verify_result (
1896 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT },
1897 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1902 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count1)
1904 auto const dir = path ("build/test/verify_valid_subtitle_line_count1");
1905 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1908 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1909 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1910 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1912 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1916 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count2)
1918 auto const dir = path ("build/test/verify_invalid_subtitle_line_count2");
1919 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1922 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
1923 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
1924 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
1925 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
1927 check_verify_result (
1930 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT },
1931 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1936 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count2)
1938 auto const dir = path ("build/test/verify_valid_subtitle_line_count2");
1939 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1942 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
1943 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
1944 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
1945 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
1947 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1951 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length1)
1953 auto const dir = path ("build/test/verify_invalid_subtitle_line_length1");
1954 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1957 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123" }
1959 check_verify_result (
1962 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH },
1963 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1968 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length2)
1970 auto const dir = path ("build/test/verify_invalid_subtitle_line_length2");
1971 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1974 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1976 check_verify_result (
1979 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH },
1980 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1985 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count1)
1987 auto const dir = path ("build/test/verify_valid_closed_caption_line_count1");
1988 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
1991 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1992 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1993 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1994 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
1996 check_verify_result (
1999 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT},
2000 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2005 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count2)
2007 auto const dir = path ("build/test/verify_valid_closed_caption_line_count2");
2008 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2011 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2012 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2013 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2015 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2019 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_count3)
2021 auto const dir = path ("build/test/verify_invalid_closed_caption_line_count3");
2022 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2025 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2026 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2027 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2028 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2030 check_verify_result (
2033 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT},
2034 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2039 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count4)
2041 auto const dir = path ("build/test/verify_valid_closed_caption_line_count4");
2042 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2045 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2046 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2047 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2048 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2050 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2054 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_length)
2056 auto const dir = path ("build/test/verify_valid_closed_caption_line_length");
2057 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2060 { 96, 300, 0.0, dcp::VAlign::CENTER, "01234567890123456789012345678901" }
2062 check_verify_result (
2065 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2070 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length)
2072 auto const dir = path ("build/test/verify_invalid_closed_caption_line_length");
2073 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2076 { 96, 300, 0.0, dcp::VAlign::CENTER, "0123456789012345678901234567890123" }
2078 check_verify_result (
2081 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH },
2082 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2087 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1)
2089 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign1");
2090 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2093 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2094 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2095 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2097 check_verify_result (
2100 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2105 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2)
2107 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign2");
2108 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2111 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2112 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2113 { 96, 300, 0.2, dcp::VAlign::CENTER, "not fine" },
2115 check_verify_result (
2118 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN },
2119 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2124 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1)
2126 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering1");
2127 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2130 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2131 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2132 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2134 check_verify_result (
2137 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2142 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2)
2144 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering2");
2145 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2148 { 96, 300, 0.2, dcp::VAlign::BOTTOM, "This" },
2149 { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" },
2150 { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" },
2152 check_verify_result (
2155 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2160 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3)
2162 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering3");
2163 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering3.xml");
2164 check_verify_result (
2167 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING },
2168 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2173 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4)
2175 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4");
2176 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml");
2177 check_verify_result (
2180 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2186 BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate)
2188 path const dir("build/test/verify_invalid_sound_frame_rate");
2189 prepare_directory (dir);
2191 auto picture = simple_picture (dir, "foo");
2192 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
2193 auto reel = make_shared<dcp::Reel>();
2194 reel->add (reel_picture);
2195 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000, boost::none);
2196 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
2197 reel->add (reel_sound);
2198 reel->add (simple_markers());
2199 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2201 auto dcp = make_shared<dcp::DCP>(dir);
2203 dcp->set_annotation_text("hello");
2206 check_verify_result (
2209 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf") },
2210 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
2215 BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text)
2217 path const dir("build/test/verify_missing_cpl_annotation_text");
2218 auto dcp = make_simple (dir);
2219 dcp->set_annotation_text("A Test DCP");
2222 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2224 auto const cpl = dcp->cpls()[0];
2227 BOOST_REQUIRE (cpl->file());
2228 Editor e(cpl->file().get());
2229 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
2232 check_verify_result (
2235 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) },
2236 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) }
2241 BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text)
2243 path const dir("build/test/verify_mismatched_cpl_annotation_text");
2244 auto dcp = make_simple (dir);
2245 dcp->set_annotation_text("A Test DCP");
2248 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2249 auto const cpl = dcp->cpls()[0];
2252 BOOST_REQUIRE (cpl->file());
2253 Editor e(cpl->file().get());
2254 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
2257 check_verify_result (
2260 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) },
2261 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) }
2266 BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration)
2268 path const dir("build/test/verify_mismatched_asset_duration");
2269 prepare_directory (dir);
2270 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
2271 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2273 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
2274 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
2276 auto reel = make_shared<dcp::Reel>(
2277 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
2278 make_shared<dcp::ReelSoundAsset>(ms, 0)
2281 reel->add (simple_markers());
2285 dcp->set_annotation_text("A Test DCP");
2288 check_verify_result (
2291 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION },
2292 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), canonical(cpl->file().get()) }
2299 shared_ptr<dcp::CPL>
2300 verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool add_to_reel2)
2302 prepare_directory (dir);
2303 auto dcp = make_shared<dcp::DCP>(dir);
2304 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2306 auto constexpr reel_length = 192;
2308 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2309 subs->set_language (dcp::LanguageTag("de-DE"));
2310 subs->set_start_time (dcp::Time());
2311 subs->add (simple_subtitle());
2312 subs->write (dir / "subs.mxf");
2313 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2315 auto reel1 = make_shared<dcp::Reel>(
2316 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2317 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2321 reel1->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2324 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2325 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2326 reel1->add (markers1);
2330 auto reel2 = make_shared<dcp::Reel>(
2331 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2332 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2336 reel2->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2339 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2340 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
2341 reel2->add (markers2);
2346 dcp->set_annotation_text("A Test DCP");
2353 BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels)
2356 path dir ("build/test/missing_main_subtitle_from_some_reels");
2357 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, false);
2358 check_verify_result (
2361 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS },
2362 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2368 path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
2369 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, true);
2370 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2374 path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
2375 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, false, false);
2376 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2382 shared_ptr<dcp::CPL>
2383 verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, int caps_in_reel2)
2385 prepare_directory (dir);
2386 auto dcp = make_shared<dcp::DCP>(dir);
2387 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2389 auto constexpr reel_length = 192;
2391 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2392 subs->set_language (dcp::LanguageTag("de-DE"));
2393 subs->set_start_time (dcp::Time());
2394 subs->add (simple_subtitle());
2395 subs->write (dir / "subs.mxf");
2397 auto reel1 = make_shared<dcp::Reel>(
2398 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2399 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2402 for (int i = 0; i < caps_in_reel1; ++i) {
2403 reel1->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2406 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2407 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2408 reel1->add (markers1);
2412 auto reel2 = make_shared<dcp::Reel>(
2413 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2414 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2417 for (int i = 0; i < caps_in_reel2; ++i) {
2418 reel2->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2421 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2422 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
2423 reel2->add (markers2);
2428 dcp->set_annotation_text("A Test DCP");
2435 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_asset_counts)
2438 path dir ("build/test/mismatched_closed_caption_asset_counts");
2439 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2440 check_verify_result (
2443 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS },
2444 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2449 path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2450 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2451 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2455 path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2456 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2457 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2464 verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2466 prepare_directory (dir);
2467 auto dcp = make_shared<dcp::DCP>(dir);
2468 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2470 auto constexpr reel_length = 192;
2472 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2473 subs->set_language (dcp::LanguageTag("de-DE"));
2474 subs->set_start_time (dcp::Time());
2475 subs->add (simple_subtitle());
2476 subs->write (dir / "subs.mxf");
2477 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), reel_length, 0);
2480 auto reel = make_shared<dcp::Reel>(
2481 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2482 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2485 reel->add (reel_text);
2487 reel->add (simple_markers(reel_length));
2492 dcp->set_annotation_text("A Test DCP");
2495 check_verify_result (
2498 { dcp::VerificationNote::Type::BV21_ERROR, code, subs->id() },
2499 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2504 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2506 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
2507 "build/test/verify_subtitle_entry_point_must_be_present",
2508 dcp::VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT,
2509 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
2510 asset->unset_entry_point ();
2514 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
2515 "build/test/verify_subtitle_entry_point_must_be_zero",
2516 dcp::VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT,
2517 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
2518 asset->set_entry_point (4);
2522 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
2523 "build/test/verify_closed_caption_entry_point_must_be_present",
2524 dcp::VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2525 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
2526 asset->unset_entry_point ();
2530 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
2531 "build/test/verify_closed_caption_entry_point_must_be_zero",
2532 dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT,
2533 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
2534 asset->set_entry_point (9);
2540 BOOST_AUTO_TEST_CASE (verify_missing_hash)
2544 path const dir("build/test/verify_missing_hash");
2545 auto dcp = make_simple (dir);
2546 dcp->set_annotation_text("A Test DCP");
2549 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2550 auto const cpl = dcp->cpls()[0];
2551 BOOST_REQUIRE_EQUAL (cpl->reels().size(), 1U);
2552 BOOST_REQUIRE (cpl->reels()[0]->main_picture());
2553 auto asset_id = cpl->reels()[0]->main_picture()->id();
2556 BOOST_REQUIRE (cpl->file());
2557 Editor e(cpl->file().get());
2558 e.delete_first_line_containing("<Hash>");
2561 check_verify_result (
2564 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2565 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id }
2572 verify_markers_test (
2574 vector<pair<dcp::Marker, dcp::Time>> markers,
2575 vector<dcp::VerificationNote> test_notes
2578 auto dcp = make_simple (dir);
2579 dcp->cpls()[0]->set_content_kind (dcp::ContentKind::FEATURE);
2580 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24);
2581 for (auto const& i: markers) {
2582 markers_asset->set (i.first, i.second);
2584 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2585 dcp->set_annotation_text("A Test DCP");
2588 check_verify_result ({dir}, test_notes);
2592 BOOST_AUTO_TEST_CASE (verify_markers)
2594 verify_markers_test (
2595 "build/test/verify_markers_all_correct",
2597 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2598 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2599 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2600 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2605 verify_markers_test (
2606 "build/test/verify_markers_missing_ffec",
2608 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2609 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2610 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2613 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }
2616 verify_markers_test (
2617 "build/test/verify_markers_missing_ffmc",
2619 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2620 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2621 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2624 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }
2627 verify_markers_test (
2628 "build/test/verify_markers_missing_ffoc",
2630 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2631 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2632 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2635 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC}
2638 verify_markers_test (
2639 "build/test/verify_markers_missing_lfoc",
2641 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2642 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2643 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2646 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }
2649 verify_markers_test (
2650 "build/test/verify_markers_incorrect_ffoc",
2652 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2653 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2654 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2655 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2658 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_FFOC, string("3") }
2661 verify_markers_test (
2662 "build/test/verify_markers_incorrect_lfoc",
2664 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2665 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2666 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2667 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2670 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_LFOC, string("18") }
2675 BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number)
2677 path dir = "build/test/verify_missing_cpl_metadata_version_number";
2678 prepare_directory (dir);
2679 auto dcp = make_simple (dir);
2680 auto cpl = dcp->cpls()[0];
2681 cpl->unset_version_number();
2682 dcp->set_annotation_text("A Test DCP");
2685 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get() }});
2689 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1)
2691 path dir = "build/test/verify_missing_extension_metadata1";
2692 auto dcp = make_simple (dir);
2693 dcp->set_annotation_text("A Test DCP");
2696 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2697 auto cpl = dcp->cpls()[0];
2700 Editor e (cpl->file().get());
2701 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2704 check_verify_result (
2707 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2708 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() }
2713 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2)
2715 path dir = "build/test/verify_missing_extension_metadata2";
2716 auto dcp = make_simple (dir);
2717 dcp->set_annotation_text("A Test DCP");
2720 auto cpl = dcp->cpls()[0];
2723 Editor e (cpl->file().get());
2724 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2727 check_verify_result (
2730 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2731 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() }
2736 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3)
2738 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata3";
2739 auto dcp = make_simple (dir);
2740 dcp->set_annotation_text("A Test DCP");
2743 auto const cpl = dcp->cpls()[0];
2746 Editor e (cpl->file().get());
2747 e.replace ("<meta:Name>A", "<meta:NameX>A");
2748 e.replace ("n</meta:Name>", "n</meta:NameX>");
2751 check_verify_result (
2754 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70 },
2755 { 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 },
2756 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2761 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1)
2763 path dir = "build/test/verify_invalid_extension_metadata1";
2764 auto dcp = make_simple (dir);
2765 dcp->set_annotation_text("A Test DCP");
2768 auto cpl = dcp->cpls()[0];
2771 Editor e (cpl->file().get());
2772 e.replace ("Application", "Fred");
2775 check_verify_result (
2778 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2779 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get() },
2784 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2)
2786 path dir = "build/test/verify_invalid_extension_metadata2";
2787 auto dcp = make_simple (dir);
2788 dcp->set_annotation_text("A Test DCP");
2791 auto cpl = dcp->cpls()[0];
2794 Editor e (cpl->file().get());
2795 e.replace ("DCP Constraints Profile", "Fred");
2798 check_verify_result (
2801 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2802 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get() },
2807 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6)
2809 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata6";
2810 auto dcp = make_simple (dir);
2811 dcp->set_annotation_text("A Test DCP");
2814 auto const cpl = dcp->cpls()[0];
2817 Editor e (cpl->file().get());
2818 e.replace ("<meta:Value>", "<meta:ValueX>");
2819 e.replace ("</meta:Value>", "</meta:ValueX>");
2822 check_verify_result (
2825 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74 },
2826 { 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 },
2827 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2832 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7)
2834 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata7";
2835 auto dcp = make_simple (dir);
2836 dcp->set_annotation_text("A Test DCP");
2839 auto const cpl = dcp->cpls()[0];
2842 Editor e (cpl->file().get());
2843 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2846 check_verify_result (
2849 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2850 { 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() },
2855 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8)
2857 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata8";
2858 auto dcp = make_simple (dir);
2859 dcp->set_annotation_text("A Test DCP");
2862 auto const cpl = dcp->cpls()[0];
2865 Editor e (cpl->file().get());
2866 e.replace ("<meta:Property>", "<meta:PropertyX>");
2867 e.replace ("</meta:Property>", "</meta:PropertyX>");
2870 check_verify_result (
2873 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72 },
2874 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyX' is not allowed for content model '(Property+)'"), cpl->file().get(), 76 },
2875 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2880 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9)
2882 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata9";
2883 auto dcp = make_simple (dir);
2884 dcp->set_annotation_text("A Test DCP");
2887 auto const cpl = dcp->cpls()[0];
2890 Editor e (cpl->file().get());
2891 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2892 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2895 check_verify_result (
2898 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71 },
2899 { 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 },
2900 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2906 BOOST_AUTO_TEST_CASE (verify_unsigned_cpl_with_encrypted_content)
2908 path dir = "build/test/verify_unsigned_cpl_with_encrypted_content";
2909 prepare_directory (dir);
2910 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
2911 copy_file (i.path(), dir / i.path().filename());
2914 path const pkl = dir / ( "pkl_" + encryption_test_pkl_id + ".xml" );
2915 path const cpl = dir / ( "cpl_" + encryption_test_cpl_id + ".xml");
2919 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2922 check_verify_result (
2925 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, encryption_test_cpl_id, canonical(cpl) },
2926 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id, canonical(pkl), },
2927 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE },
2928 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE },
2929 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC },
2930 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC },
2931 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id, canonical(cpl) },
2932 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, encryption_test_cpl_id, canonical(cpl) }
2937 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_encrypted_content)
2939 path dir = "build/test/unsigned_pkl_with_encrypted_content";
2940 prepare_directory (dir);
2941 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
2942 copy_file (i.path(), dir / i.path().filename());
2945 path const cpl = dir / ("cpl_" + encryption_test_cpl_id + ".xml");
2946 path const pkl = dir / ("pkl_" + encryption_test_pkl_id + ".xml");
2949 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2952 check_verify_result (
2955 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id, canonical(pkl) },
2956 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE },
2957 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE },
2958 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC },
2959 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC },
2960 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id, canonical(cpl) },
2961 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id, canonical(pkl) },
2966 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_unencrypted_content)
2968 path dir = "build/test/verify_unsigned_pkl_with_unencrypted_content";
2969 prepare_directory (dir);
2970 for (auto i: directory_iterator("test/ref/DCP/dcp_test1")) {
2971 copy_file (i.path(), dir / i.path().filename());
2975 Editor e (dir / dcp_test1_pkl);
2976 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2979 check_verify_result ({dir}, {});
2983 BOOST_AUTO_TEST_CASE (verify_partially_encrypted)
2985 path dir ("build/test/verify_must_not_be_partially_encrypted");
2986 prepare_directory (dir);
2990 auto signer = make_shared<dcp::CertificateChain>();
2991 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
2992 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
2993 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
2994 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
2996 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
3000 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
3003 auto writer = mp->start_write (dir / "video.mxf", false);
3004 dcp::ArrayData j2c ("test/data/flat_red.j2c");
3005 for (int i = 0; i < 24; ++i) {
3006 writer->write (j2c.data(), j2c.size());
3008 writer->finalize ();
3010 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
3012 auto reel = make_shared<dcp::Reel>(
3013 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
3014 make_shared<dcp::ReelSoundAsset>(ms, 0)
3017 reel->add (simple_markers());
3021 cpl->set_content_version (
3022 {"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"}
3024 cpl->set_annotation_text ("A Test DCP");
3025 cpl->set_issuer ("OpenDCP 0.0.25");
3026 cpl->set_creator ("OpenDCP 0.0.25");
3027 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
3028 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
3029 cpl->set_main_sound_sample_rate (48000);
3030 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
3031 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
3032 cpl->set_version_number (1);
3036 d.set_issuer("OpenDCP 0.0.25");
3037 d.set_creator("OpenDCP 0.0.25");
3038 d.set_issue_date("2012-07-17T04:45:18+00:00");
3039 d.set_annotation_text("A Test DCP");
3040 d.write_xml(signer);
3042 check_verify_result (
3045 {dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED},
3050 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k)
3052 vector<dcp::VerificationNote> notes;
3053 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"));
3054 auto reader = picture.start_read ();
3055 auto frame = reader->get_frame (0);
3056 verify_j2k (frame, notes);
3057 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3061 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k)
3063 vector<dcp::VerificationNote> notes;
3064 dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR"));
3065 auto reader = picture.start_read ();
3066 auto frame = reader->get_frame (0);
3067 verify_j2k (frame, notes);
3068 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3072 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp)
3074 boost::filesystem::path dir = "build/test/verify_jpeg2000_codestream_libdcp";
3075 prepare_directory (dir);
3076 auto dcp = make_simple (dir);
3078 vector<dcp::VerificationNote> notes;
3079 dcp::MonoPictureAsset picture (find_file(dir, "video"));
3080 auto reader = picture.start_read ();
3081 auto frame = reader->get_frame (0);
3082 verify_j2k (frame, notes);
3083 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3087 /** Check that ResourceID and the XML ID being different is spotted */
3088 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id)
3090 boost::filesystem::path const dir = "build/test/verify_mismatched_subtitle_resource_id";
3091 prepare_directory (dir);
3093 ASDCP::WriterInfo writer_info;
3094 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
3097 auto mxf_id = dcp::make_uuid ();
3098 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
3099 BOOST_REQUIRE (c == Kumu::UUID_Length);
3101 auto resource_id = dcp::make_uuid ();
3102 ASDCP::TimedText::TimedTextDescriptor descriptor;
3103 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
3104 DCP_ASSERT (c == Kumu::UUID_Length);
3106 auto xml_id = dcp::make_uuid ();
3107 ASDCP::TimedText::MXFWriter writer;
3108 auto subs_mxf = dir / "subs.mxf";
3109 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
3110 BOOST_REQUIRE (ASDCP_SUCCESS(r));
3111 writer.WriteTimedTextResource (dcp::String::compose(
3112 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
3113 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
3114 "<Id>urn:uuid:%1</Id>"
3115 "<ContentTitleText>Content</ContentTitleText>"
3116 "<AnnotationText>Annotation</AnnotationText>"
3117 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
3118 "<ReelNumber>1</ReelNumber>"
3119 "<Language>en-US</Language>"
3120 "<EditRate>25 1</EditRate>"
3121 "<TimeCodeRate>25</TimeCodeRate>"
3122 "<StartTime>00:00:00:00</StartTime>"
3124 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
3125 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
3126 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
3135 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
3136 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
3138 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
3140 check_verify_result (
3143 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) },
3144 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID },
3145 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
3146 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
3151 /** Check that ResourceID and the MXF ID being the same is spotted */
3152 BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id)
3154 boost::filesystem::path const dir = "build/test/verify_incorrect_timed_text_id";
3155 prepare_directory (dir);
3157 ASDCP::WriterInfo writer_info;
3158 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
3161 auto mxf_id = dcp::make_uuid ();
3162 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
3163 BOOST_REQUIRE (c == Kumu::UUID_Length);
3165 auto resource_id = mxf_id;
3166 ASDCP::TimedText::TimedTextDescriptor descriptor;
3167 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
3168 DCP_ASSERT (c == Kumu::UUID_Length);
3170 auto xml_id = resource_id;
3171 ASDCP::TimedText::MXFWriter writer;
3172 auto subs_mxf = dir / "subs.mxf";
3173 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
3174 BOOST_REQUIRE (ASDCP_SUCCESS(r));
3175 writer.WriteTimedTextResource (dcp::String::compose(
3176 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
3177 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
3178 "<Id>urn:uuid:%1</Id>"
3179 "<ContentTitleText>Content</ContentTitleText>"
3180 "<AnnotationText>Annotation</AnnotationText>"
3181 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
3182 "<ReelNumber>1</ReelNumber>"
3183 "<Language>en-US</Language>"
3184 "<EditRate>25 1</EditRate>"
3185 "<TimeCodeRate>25</TimeCodeRate>"
3186 "<StartTime>00:00:00:00</StartTime>"
3188 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
3189 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
3190 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
3199 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
3200 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
3202 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
3204 check_verify_result (
3207 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) },
3208 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID },
3209 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
3210 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
3215 /** Check a DCP with a 3D asset marked as 2D */
3216 BOOST_AUTO_TEST_CASE (verify_threed_marked_as_twod)
3218 check_verify_result (
3219 { private_test / "data" / "xm" },
3222 dcp::VerificationNote::Type::WARNING,
3223 dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(private_test / "data" / "xm", "j2c"))
3226 dcp::VerificationNote::Type::BV21_ERROR,
3227 dcp::VerificationNote::Code::INVALID_STANDARD
3234 BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers)
3236 path dir = "build/test/verify_unexpected_things_in_main_markers";
3237 prepare_directory (dir);
3238 auto dcp = make_simple (dir, 1, 24);
3239 dcp->set_annotation_text("A Test DCP");
3243 Editor e (find_cpl(dir));
3245 " <IntrinsicDuration>24</IntrinsicDuration>",
3246 "<EntryPoint>0</EntryPoint><Duration>24</Duration>"
3250 dcp::CPL cpl (find_cpl(dir));
3252 check_verify_result (
3255 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3256 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT },
3257 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION },
3262 BOOST_AUTO_TEST_CASE(verify_invalid_content_kind)
3264 path dir = "build/test/verify_invalid_content_kind";
3265 prepare_directory (dir);
3266 auto dcp = make_simple (dir, 1, 24);
3267 dcp->set_annotation_text("A Test DCP");
3271 Editor e(find_cpl(dir));
3272 e.replace("trailer", "trip");
3275 dcp::CPL cpl (find_cpl(dir));
3277 check_verify_result (
3280 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3281 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip") }
3287 BOOST_AUTO_TEST_CASE(verify_valid_content_kind)
3289 path dir = "build/test/verify_valid_content_kind";
3290 prepare_directory (dir);
3291 auto dcp = make_simple (dir, 1, 24);
3292 dcp->set_annotation_text("A Test DCP");
3296 Editor e(find_cpl(dir));
3297 e.replace("<ContentKind>trailer</ContentKind>", "<ContentKind scope=\"http://bobs.contents/\">trip</ContentKind>");
3300 dcp::CPL cpl (find_cpl(dir));
3302 check_verify_result (
3305 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3311 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1)
3313 path dir = "build/test/verify_invalid_main_picture_active_area_1";
3314 prepare_directory(dir);
3315 auto dcp = make_simple(dir, 1, 24);
3318 auto constexpr area = "<meta:MainPictureActiveArea>";
3321 Editor e(find_cpl(dir));
3322 e.delete_lines_after(area, 2);
3323 e.insert(area, "<meta:Height>4080</meta:Height>");
3324 e.insert(area, "<meta:Width>1997</meta:Width>");
3327 dcp::PKL pkl(find_pkl(dir));
3328 dcp::CPL cpl(find_cpl(dir));
3330 check_verify_result(
3333 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3334 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3335 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir)) },
3336 { 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)) },
3341 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2)
3343 path dir = "build/test/verify_invalid_main_picture_active_area_2";
3344 prepare_directory(dir);
3345 auto dcp = make_simple(dir, 1, 24);
3348 auto constexpr area = "<meta:MainPictureActiveArea>";
3351 Editor e(find_cpl(dir));
3352 e.delete_lines_after(area, 2);
3353 e.insert(area, "<meta:Height>5125</meta:Height>");
3354 e.insert(area, "<meta:Width>9900</meta:Width>");
3357 dcp::PKL pkl(find_pkl(dir));
3358 dcp::CPL cpl(find_cpl(dir));
3360 check_verify_result(
3363 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3364 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3365 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir)) },
3366 { 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)) },
3367 { 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)) },
3372 BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids)
3376 path dir = "build/test/verify_duplicate_pkl_asset_ids";
3377 prepare_directory(dir);
3378 auto dcp = make_simple(dir, 1, 24);
3382 Editor e(find_pkl(dir));
3383 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab");
3386 dcp::PKL pkl(find_pkl(dir));
3388 check_verify_result(
3391 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) },
3396 BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids)
3400 path dir = "build/test/verify_duplicate_assetmap_asset_ids";
3401 prepare_directory(dir);
3402 auto dcp = make_simple(dir, 1, 24);
3406 Editor e(find_asset_map(dir));
3407 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa");
3410 dcp::PKL pkl(find_pkl(dir));
3411 dcp::AssetMap asset_map(find_asset_map(dir));
3413 check_verify_result(
3416 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3417 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir)) },
3418 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54") },