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::BV21_ERROR, dcp::VerificationNote::Code::INVALID_STANDARD },
922 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EMPTY_TEXT },
927 BOOST_AUTO_TEST_CASE (verify_external_asset)
929 path const ov_dir("build/test/verify_external_asset");
930 prepare_directory (ov_dir);
932 auto image = black_image ();
933 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
934 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
935 dcp_from_frame (frame, ov_dir);
937 dcp::DCP ov (ov_dir);
940 path const vf_dir("build/test/verify_external_asset_vf");
941 prepare_directory (vf_dir);
943 auto picture = ov.cpls()[0]->reels()[0]->main_picture();
944 auto cpl = write_dcp_with_single_asset (vf_dir, picture);
946 check_verify_result (
949 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, picture->asset()->id() },
950 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
955 BOOST_AUTO_TEST_CASE (verify_valid_cpl_metadata)
957 path const dir("build/test/verify_valid_cpl_metadata");
958 prepare_directory (dir);
960 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
961 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
962 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
964 auto reel = make_shared<dcp::Reel>();
965 reel->add (reel_asset);
967 reel->add (make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 16 * 24), 0));
968 reel->add (simple_markers(16 * 24));
970 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
972 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
973 cpl->set_main_sound_sample_rate (48000);
974 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
975 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
976 cpl->set_version_number (1);
980 dcp.set_annotation_text("hello");
986 find_prefix(path dir, string prefix)
988 auto iter = std::find_if(directory_iterator(dir), directory_iterator(), [prefix](path const& p) {
989 return boost::starts_with(p.filename().string(), prefix);
992 BOOST_REQUIRE(iter != directory_iterator());
997 path find_cpl (path dir)
999 return find_prefix(dir, "cpl_");
1006 return find_prefix(dir, "pkl_");
1011 find_asset_map(path dir)
1013 return find_prefix(dir, "ASSETMAP");
1017 /* DCP with invalid CompositionMetadataAsset */
1018 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_bad_tag)
1020 using namespace boost::filesystem;
1022 path const dir("build/test/verify_invalid_cpl_metadata_bad_tag");
1023 prepare_directory (dir);
1025 auto reel = make_shared<dcp::Reel>();
1026 reel->add (black_picture_asset(dir));
1027 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1029 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1030 cpl->set_main_sound_sample_rate (48000);
1031 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1032 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1033 cpl->set_version_number (1);
1035 reel->add (simple_markers());
1039 dcp.set_annotation_text("hello");
1043 Editor e (find_cpl(dir));
1044 e.replace ("MainSound", "MainSoundX");
1047 check_verify_result (
1050 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXConfiguration'"), canonical(cpl->file().get()), 50 },
1051 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:MainSoundXSampleRate'"), canonical(cpl->file().get()), 51 },
1053 dcp::VerificationNote::Type::ERROR,
1054 dcp::VerificationNote::Code::INVALID_XML,
1055 string("element 'meta:MainSoundXConfiguration' is not allowed for content model "
1056 "'(Id,AnnotationText?,EditRate,IntrinsicDuration,EntryPoint?,Duration?,"
1057 "FullContentTitleText,ReleaseTerritory?,VersionNumber?,Chain?,Distributor?,"
1058 "Facility?,AlternateContentVersionList?,Luminance?,MainSoundConfiguration,"
1059 "MainSoundSampleRate,MainPictureStoredArea,MainPictureActiveArea,MainSubtitleLanguageList?,"
1060 "ExtensionMetadataList?,)'"),
1061 canonical(cpl->file().get()),
1064 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) },
1069 /* DCP with invalid CompositionMetadataAsset */
1070 BOOST_AUTO_TEST_CASE (verify_invalid_cpl_metadata_missing_tag)
1072 path const dir("build/test/verify_invalid_cpl_metadata_missing_tag");
1073 prepare_directory (dir);
1075 auto reel = make_shared<dcp::Reel>();
1076 reel->add (black_picture_asset(dir));
1077 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1079 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1080 cpl->set_main_sound_sample_rate (48000);
1081 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1082 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1086 dcp.set_annotation_text("hello");
1090 Editor e (find_cpl(dir));
1091 e.replace ("meta:Width", "meta:WidthX");
1094 check_verify_result (
1096 {{ dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::FAILED_READ, string("missing XML tag Width in MainPictureStoredArea") }}
1101 BOOST_AUTO_TEST_CASE (verify_invalid_language1)
1103 path const dir("build/test/verify_invalid_language1");
1104 prepare_directory (dir);
1105 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1106 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1107 asset->_language = "wrong-andbad";
1108 asset->write (dir / "subs.mxf");
1109 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1110 reel_asset->_language = "badlang";
1111 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1113 check_verify_result (
1116 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") },
1117 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") },
1118 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1123 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
1124 BOOST_AUTO_TEST_CASE (verify_invalid_language2)
1126 path const dir("build/test/verify_invalid_language2");
1127 prepare_directory (dir);
1128 copy_file ("test/data/subs.mxf", dir / "subs.mxf");
1129 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
1130 asset->_language = "wrong-andbad";
1131 asset->write (dir / "subs.mxf");
1132 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 6046, 0);
1133 reel_asset->_language = "badlang";
1134 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1136 check_verify_result (
1139 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("badlang") },
1140 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("wrong-andbad") },
1141 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1146 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
1147 * the release territory.
1149 BOOST_AUTO_TEST_CASE (verify_invalid_language3)
1151 path const dir("build/test/verify_invalid_language3");
1152 prepare_directory (dir);
1154 auto picture = simple_picture (dir, "foo");
1155 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1156 auto reel = make_shared<dcp::Reel>();
1157 reel->add (reel_picture);
1158 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
1159 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1160 reel->add (reel_sound);
1161 reel->add (simple_markers());
1163 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1165 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
1166 cpl->_additional_subtitle_languages.push_back("andso-is-this");
1167 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1168 cpl->set_main_sound_sample_rate (48000);
1169 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1170 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
1171 cpl->set_version_number (1);
1172 cpl->_release_territory = "fred-jim";
1173 auto dcp = make_shared<dcp::DCP>(dir);
1175 dcp->set_annotation_text("hello");
1178 check_verify_result (
1181 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("this-is-wrong") },
1182 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("andso-is-this") },
1183 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("fred-jim") },
1184 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_LANGUAGE, string("frobozz") },
1190 vector<dcp::VerificationNote>
1191 check_picture_size (int width, int height, int frame_rate, bool three_d)
1193 using namespace boost::filesystem;
1195 path dcp_path = "build/test/verify_picture_test";
1196 prepare_directory (dcp_path);
1198 shared_ptr<dcp::PictureAsset> mp;
1200 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1202 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::Standard::SMPTE);
1204 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
1206 auto image = black_image (dcp::Size(width, height));
1207 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1208 int const length = three_d ? frame_rate * 2 : frame_rate;
1209 for (int i = 0; i < length; ++i) {
1210 picture_writer->write (j2c.data(), j2c.size());
1212 picture_writer->finalize ();
1214 auto d = make_shared<dcp::DCP>(dcp_path);
1215 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1216 cpl->set_annotation_text ("A Test DCP");
1217 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1218 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1219 cpl->set_main_sound_sample_rate (48000);
1220 cpl->set_main_picture_stored_area(dcp::Size(width, height));
1221 cpl->set_main_picture_active_area(dcp::Size(width, height));
1222 cpl->set_version_number (1);
1224 auto reel = make_shared<dcp::Reel>();
1227 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1229 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1232 reel->add (simple_markers(frame_rate));
1237 d->set_annotation_text("A Test DCP");
1240 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1246 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1248 auto notes = check_picture_size(width, height, frame_rate, three_d);
1249 BOOST_CHECK_EQUAL (notes.size(), 0U);
1255 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1257 auto notes = check_picture_size(width, height, frame_rate, three_d);
1258 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1259 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1260 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_SIZE_IN_PIXELS);
1266 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1268 auto notes = check_picture_size(width, height, frame_rate, three_d);
1269 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1270 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::Type::BV21_ERROR);
1271 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_2K);
1277 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1279 auto notes = check_picture_size(width, height, frame_rate, three_d);
1280 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1281 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1282 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_FRAME_RATE_FOR_4K);
1286 BOOST_AUTO_TEST_CASE (verify_picture_size)
1288 using namespace boost::filesystem;
1291 check_picture_size_ok (2048, 858, 24, false);
1292 check_picture_size_ok (2048, 858, 25, false);
1293 check_picture_size_ok (2048, 858, 48, false);
1294 check_picture_size_ok (2048, 858, 24, true);
1295 check_picture_size_ok (2048, 858, 25, true);
1296 check_picture_size_ok (2048, 858, 48, true);
1299 check_picture_size_ok (1998, 1080, 24, false);
1300 check_picture_size_ok (1998, 1080, 25, false);
1301 check_picture_size_ok (1998, 1080, 48, false);
1302 check_picture_size_ok (1998, 1080, 24, true);
1303 check_picture_size_ok (1998, 1080, 25, true);
1304 check_picture_size_ok (1998, 1080, 48, true);
1307 check_picture_size_ok (4096, 1716, 24, false);
1310 check_picture_size_ok (3996, 2160, 24, false);
1312 /* Bad frame size */
1313 check_picture_size_bad_frame_size (2050, 858, 24, false);
1314 check_picture_size_bad_frame_size (2048, 658, 25, false);
1315 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1316 check_picture_size_bad_frame_size (4000, 2000, 24, true);
1318 /* Bad 2K frame rate */
1319 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1320 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1321 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1323 /* Bad 4K frame rate */
1324 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1325 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1328 auto notes = check_picture_size(3996, 2160, 24, true);
1329 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1330 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::Type::BV21_ERROR);
1331 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::Code::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D);
1337 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")
1340 std::make_shared<dcp::SubtitleString>(
1348 dcp::Time(start_frame, 24, 24),
1349 dcp::Time(end_frame, 24, 24),
1351 dcp::HAlign::CENTER,
1355 dcp::Direction::LTR,
1367 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_xml_size_in_bytes)
1369 path const dir("build/test/verify_invalid_closed_caption_xml_size_in_bytes");
1370 prepare_directory (dir);
1372 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1373 for (int i = 0; i < 2048; ++i) {
1374 add_test_subtitle (asset, i * 24, i * 24 + 20);
1376 asset->set_language (dcp::LanguageTag("de-DE"));
1377 asset->write (dir / "subs.mxf");
1378 auto reel_asset = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 49148, 0);
1379 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1381 check_verify_result (
1384 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1386 dcp::VerificationNote::Type::BV21_ERROR,
1387 dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES,
1389 canonical(dir / "subs.mxf")
1391 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1392 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1398 shared_ptr<dcp::SMPTESubtitleAsset>
1399 make_large_subtitle_asset (path font_file)
1401 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1402 dcp::ArrayData big_fake_font(1024 * 1024);
1403 big_fake_font.write (font_file);
1404 for (int i = 0; i < 116; ++i) {
1405 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1413 verify_timed_text_asset_too_large (string name)
1415 auto const dir = path("build/test") / name;
1416 prepare_directory (dir);
1417 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1418 add_test_subtitle (asset, 0, 240);
1419 asset->set_language (dcp::LanguageTag("de-DE"));
1420 asset->write (dir / "subs.mxf");
1422 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 240, 0);
1423 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1425 check_verify_result (
1428 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_SIZE_IN_BYTES, string("121695542"), canonical(dir / "subs.mxf") },
1429 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES, string("121634816"), canonical(dir / "subs.mxf") },
1430 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1431 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1432 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
1437 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1439 verify_timed_text_asset_too_large<dcp::ReelSMPTESubtitleAsset>("verify_subtitle_asset_too_large");
1440 verify_timed_text_asset_too_large<dcp::ReelSMPTEClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1444 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_language)
1446 path dir = "build/test/verify_missing_subtitle_language";
1447 prepare_directory (dir);
1448 auto dcp = make_simple (dir, 1, 106);
1451 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1452 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1453 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1454 "<ContentTitleText>Content</ContentTitleText>"
1455 "<AnnotationText>Annotation</AnnotationText>"
1456 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1457 "<ReelNumber>1</ReelNumber>"
1458 "<EditRate>24 1</EditRate>"
1459 "<TimeCodeRate>24</TimeCodeRate>"
1460 "<StartTime>00:00:00:00</StartTime>"
1461 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1463 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1464 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1465 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1471 dcp::File xml_file(dir / "subs.xml", "w");
1472 BOOST_REQUIRE (xml_file);
1473 xml_file.write(xml.c_str(), xml.size(), 1);
1475 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1476 subs->write (dir / "subs.mxf");
1478 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1479 dcp->cpls()[0]->reels()[0]->add(reel_subs);
1480 dcp->set_annotation_text("A Test DCP");
1483 check_verify_result (
1486 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_LANGUAGE, canonical(dir / "subs.mxf") },
1487 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1492 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_languages)
1494 path path ("build/test/verify_mismatched_subtitle_languages");
1495 auto constexpr reel_length = 192;
1496 auto dcp = make_simple (path, 2, reel_length);
1497 auto cpl = dcp->cpls()[0];
1500 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1501 subs->set_language (dcp::LanguageTag("de-DE"));
1502 subs->add (simple_subtitle());
1503 subs->write (path / "subs1.mxf");
1504 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
1505 cpl->reels()[0]->add(reel_subs);
1509 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1510 subs->set_language (dcp::LanguageTag("en-US"));
1511 subs->add (simple_subtitle());
1512 subs->write (path / "subs2.mxf");
1513 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
1514 cpl->reels()[1]->add(reel_subs);
1517 dcp->set_annotation_text("A Test DCP");
1520 check_verify_result (
1523 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") },
1524 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") },
1525 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_SUBTITLE_LANGUAGES }
1530 BOOST_AUTO_TEST_CASE (verify_multiple_closed_caption_languages_allowed)
1532 path path ("build/test/verify_multiple_closed_caption_languages_allowed");
1533 auto constexpr reel_length = 192;
1534 auto dcp = make_simple (path, 2, reel_length);
1535 auto cpl = dcp->cpls()[0];
1538 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
1539 ccaps->set_language (dcp::LanguageTag("de-DE"));
1540 ccaps->add (simple_subtitle());
1541 ccaps->write (path / "subs1.mxf");
1542 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
1543 cpl->reels()[0]->add(reel_ccaps);
1547 auto ccaps = make_shared<dcp::SMPTESubtitleAsset>();
1548 ccaps->set_language (dcp::LanguageTag("en-US"));
1549 ccaps->add (simple_subtitle());
1550 ccaps->write (path / "subs2.mxf");
1551 auto reel_ccaps = make_shared<dcp::ReelSMPTEClosedCaptionAsset>(ccaps, dcp::Fraction(24, 1), reel_length, 0);
1552 cpl->reels()[1]->add(reel_ccaps);
1555 dcp->set_annotation_text("A Test DCP");
1558 check_verify_result (
1561 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs1.mxf") },
1562 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(path / "subs2.mxf") }
1567 BOOST_AUTO_TEST_CASE (verify_missing_subtitle_start_time)
1569 path dir = "build/test/verify_missing_subtitle_start_time";
1570 prepare_directory (dir);
1571 auto dcp = make_simple (dir, 1, 106);
1574 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1575 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1576 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1577 "<ContentTitleText>Content</ContentTitleText>"
1578 "<AnnotationText>Annotation</AnnotationText>"
1579 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1580 "<ReelNumber>1</ReelNumber>"
1581 "<Language>de-DE</Language>"
1582 "<EditRate>24 1</EditRate>"
1583 "<TimeCodeRate>24</TimeCodeRate>"
1584 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1586 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1587 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1588 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1594 dcp::File xml_file(dir / "subs.xml", "w");
1595 BOOST_REQUIRE (xml_file);
1596 xml_file.write(xml.c_str(), xml.size(), 1);
1598 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1599 subs->write (dir / "subs.mxf");
1601 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1602 dcp->cpls()[0]->reels()[0]->add(reel_subs);
1603 dcp->set_annotation_text("A Test DCP");
1606 check_verify_result (
1609 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1610 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1615 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_start_time)
1617 path dir = "build/test/verify_invalid_subtitle_start_time";
1618 prepare_directory (dir);
1619 auto dcp = make_simple (dir, 1, 106);
1622 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1623 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1624 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1625 "<ContentTitleText>Content</ContentTitleText>"
1626 "<AnnotationText>Annotation</AnnotationText>"
1627 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1628 "<ReelNumber>1</ReelNumber>"
1629 "<Language>de-DE</Language>"
1630 "<EditRate>24 1</EditRate>"
1631 "<TimeCodeRate>24</TimeCodeRate>"
1632 "<StartTime>00:00:02:00</StartTime>"
1633 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1635 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1636 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1637 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1643 dcp::File xml_file(dir / "subs.xml", "w");
1644 BOOST_REQUIRE (xml_file);
1645 xml_file.write(xml.c_str(), xml.size(), 1);
1647 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1648 subs->write (dir / "subs.mxf");
1650 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), 106, 0);
1651 dcp->cpls().front()->reels().front()->add(reel_subs);
1652 dcp->set_annotation_text("A Test DCP");
1655 check_verify_result (
1658 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SUBTITLE_START_TIME, canonical(dir / "subs.mxf") },
1659 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1667 TestText (int in_, int out_, float v_position_ = 0, dcp::VAlign v_align_ = dcp::VAlign::CENTER, string text_ = "Hello")
1670 , v_position(v_position_)
1678 dcp::VAlign v_align;
1684 shared_ptr<dcp::CPL>
1685 dcp_with_text (path dir, vector<TestText> subs)
1687 prepare_directory (dir);
1688 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1689 asset->set_start_time (dcp::Time());
1690 for (auto i: subs) {
1691 add_test_subtitle (asset, i.in, i.out, i.v_position, i.v_align, i.text);
1693 asset->set_language (dcp::LanguageTag("de-DE"));
1694 asset->write (dir / "subs.mxf");
1696 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
1697 return write_dcp_with_single_asset (dir, reel_asset);
1702 shared_ptr<dcp::CPL>
1703 dcp_with_text_from_file (path dir, boost::filesystem::path subs_xml)
1705 prepare_directory (dir);
1706 auto asset = make_shared<dcp::SMPTESubtitleAsset>(subs_xml);
1707 asset->set_start_time (dcp::Time());
1708 asset->set_language (dcp::LanguageTag("de-DE"));
1710 auto subs_mxf = dir / "subs.mxf";
1711 asset->write (subs_mxf);
1713 /* The call to write() puts the asset into the DCP correctly but it will have
1714 * XML re-written by our parser. Overwrite the MXF using the given file's verbatim
1717 ASDCP::TimedText::MXFWriter writer;
1718 ASDCP::WriterInfo writer_info;
1719 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
1721 Kumu::hex2bin (asset->id().c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
1722 DCP_ASSERT (c == Kumu::UUID_Length);
1723 ASDCP::TimedText::TimedTextDescriptor descriptor;
1724 descriptor.ContainerDuration = asset->intrinsic_duration();
1725 Kumu::hex2bin (asset->xml_id()->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c);
1726 DCP_ASSERT (c == Kumu::UUID_Length);
1727 ASDCP::Result_t r = writer.OpenWrite (subs_mxf.string().c_str(), writer_info, descriptor, 16384);
1728 BOOST_REQUIRE (!ASDCP_FAILURE(r));
1729 r = writer.WriteTimedTextResource (dcp::file_to_string(subs_xml));
1730 BOOST_REQUIRE (!ASDCP_FAILURE(r));
1733 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0);
1734 return write_dcp_with_single_asset (dir, reel_asset);
1738 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time)
1740 auto const dir = path("build/test/verify_invalid_subtitle_first_text_time");
1741 /* Just too early */
1742 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1743 check_verify_result (
1746 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1747 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1753 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time)
1755 auto const dir = path("build/test/verify_valid_subtitle_first_text_time");
1756 /* Just late enough */
1757 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1758 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1762 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_first_text_time_on_second_reel)
1764 auto const dir = path("build/test/verify_valid_subtitle_first_text_time_on_second_reel");
1765 prepare_directory (dir);
1767 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1768 asset1->set_start_time (dcp::Time());
1769 /* Just late enough */
1770 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1771 asset1->set_language (dcp::LanguageTag("de-DE"));
1772 asset1->write (dir / "subs1.mxf");
1773 auto reel_asset1 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset1, dcp::Fraction(24, 1), 5 * 24, 0);
1774 auto reel1 = make_shared<dcp::Reel>();
1775 reel1->add (reel_asset1);
1776 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 5 * 24);
1777 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1778 reel1->add (markers1);
1780 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1781 asset2->set_start_time (dcp::Time());
1782 /* This would be too early on first reel but should be OK on the second */
1783 add_test_subtitle (asset2, 3, 4 * 24);
1784 asset2->set_language (dcp::LanguageTag("de-DE"));
1785 asset2->write (dir / "subs2.mxf");
1786 auto reel_asset2 = make_shared<dcp::ReelSMPTESubtitleAsset>(asset2, dcp::Fraction(24, 1), 4 * 24, 0);
1787 auto reel2 = make_shared<dcp::Reel>();
1788 reel2->add (reel_asset2);
1789 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 4 * 24);
1790 markers2->set (dcp::Marker::LFOC, dcp::Time(4 * 24 - 1, 24, 24));
1791 reel2->add (markers2);
1793 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
1796 auto dcp = make_shared<dcp::DCP>(dir);
1798 dcp->set_annotation_text("hello");
1801 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1805 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_spacing)
1807 auto const dir = path("build/test/verify_invalid_subtitle_spacing");
1808 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1812 { 5 * 24 + 1, 6 * 24 },
1814 check_verify_result (
1817 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_SPACING },
1818 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1823 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_spacing)
1825 auto const dir = path("build/test/verify_valid_subtitle_spacing");
1826 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1830 { 5 * 24 + 16, 8 * 24 },
1832 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1836 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_duration)
1838 auto const dir = path("build/test/verify_invalid_subtitle_duration");
1839 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1840 check_verify_result (
1843 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_DURATION },
1844 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1849 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_duration)
1851 auto const dir = path("build/test/verify_valid_subtitle_duration");
1852 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1853 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1857 BOOST_AUTO_TEST_CASE (verify_subtitle_overlapping_reel_boundary)
1859 auto const dir = path("build/test/verify_subtitle_overlapping_reel_boundary");
1860 prepare_directory (dir);
1861 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1862 asset->set_start_time (dcp::Time());
1863 add_test_subtitle (asset, 0, 4 * 24);
1864 asset->set_language (dcp::LanguageTag("de-DE"));
1865 asset->write (dir / "subs.mxf");
1867 auto reel_asset = make_shared<dcp::ReelSMPTESubtitleAsset>(asset, dcp::Fraction(24, 1), 3 * 24, 0);
1868 auto cpl = write_dcp_with_single_asset (dir, reel_asset);
1869 check_verify_result (
1872 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "72 96", boost::filesystem::canonical(asset->file().get()) },
1873 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1874 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::SUBTITLE_OVERLAPS_REEL_BOUNDARY },
1875 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1881 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count1)
1883 auto const dir = path ("build/test/invalid_subtitle_line_count1");
1884 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1887 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1888 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1889 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1890 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
1892 check_verify_result (
1895 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT },
1896 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1901 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count1)
1903 auto const dir = path ("build/test/verify_valid_subtitle_line_count1");
1904 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1907 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1908 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1909 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1911 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1915 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_count2)
1917 auto const dir = path ("build/test/verify_invalid_subtitle_line_count2");
1918 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1921 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
1922 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
1923 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
1924 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
1926 check_verify_result (
1929 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_COUNT },
1930 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1935 BOOST_AUTO_TEST_CASE (verify_valid_subtitle_line_count2)
1937 auto const dir = path ("build/test/verify_valid_subtitle_line_count2");
1938 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1941 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
1942 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
1943 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
1944 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
1946 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
1950 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length1)
1952 auto const dir = path ("build/test/verify_invalid_subtitle_line_length1");
1953 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1956 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123" }
1958 check_verify_result (
1961 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::NEARLY_INVALID_SUBTITLE_LINE_LENGTH },
1962 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1967 BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_line_length2)
1969 auto const dir = path ("build/test/verify_invalid_subtitle_line_length2");
1970 auto cpl = dcp_with_text<dcp::ReelSMPTESubtitleAsset> (
1973 { 96, 300, 0.0, dcp::VAlign::CENTER, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1975 check_verify_result (
1978 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_LINE_LENGTH },
1979 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
1984 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count1)
1986 auto const dir = path ("build/test/verify_valid_closed_caption_line_count1");
1987 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
1990 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
1991 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
1992 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
1993 { 96, 200, 0.3, dcp::VAlign::CENTER, "lines" }
1995 check_verify_result (
1998 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT},
1999 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2004 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count2)
2006 auto const dir = path ("build/test/verify_valid_closed_caption_line_count2");
2007 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2010 { 96, 200, 0.0, dcp::VAlign::CENTER, "We" },
2011 { 96, 200, 0.1, dcp::VAlign::CENTER, "have" },
2012 { 96, 200, 0.2, dcp::VAlign::CENTER, "four" },
2014 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2018 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_count3)
2020 auto const dir = path ("build/test/verify_invalid_closed_caption_line_count3");
2021 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2024 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2025 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2026 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2027 { 150, 180, 0.3, dcp::VAlign::CENTER, "lines" }
2029 check_verify_result (
2032 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_COUNT},
2033 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2038 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_count4)
2040 auto const dir = path ("build/test/verify_valid_closed_caption_line_count4");
2041 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2044 { 96, 300, 0.0, dcp::VAlign::CENTER, "We" },
2045 { 96, 300, 0.1, dcp::VAlign::CENTER, "have" },
2046 { 150, 180, 0.2, dcp::VAlign::CENTER, "four" },
2047 { 190, 250, 0.3, dcp::VAlign::CENTER, "lines" }
2049 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2053 BOOST_AUTO_TEST_CASE (verify_valid_closed_caption_line_length)
2055 auto const dir = path ("build/test/verify_valid_closed_caption_line_length");
2056 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2059 { 96, 300, 0.0, dcp::VAlign::CENTER, "01234567890123456789012345678901" }
2061 check_verify_result (
2064 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2069 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length)
2071 auto const dir = path ("build/test/verify_invalid_closed_caption_line_length");
2072 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2075 { 96, 300, 0.0, dcp::VAlign::CENTER, "0123456789012345678901234567890123" }
2077 check_verify_result (
2080 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_CLOSED_CAPTION_LINE_LENGTH },
2081 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2086 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1)
2088 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign1");
2089 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2092 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2093 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2094 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2096 check_verify_result (
2099 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2104 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2)
2106 auto const dir = path ("build/test/verify_mismatched_closed_caption_valign2");
2107 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2110 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2111 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2112 { 96, 300, 0.2, dcp::VAlign::CENTER, "not fine" },
2114 check_verify_result (
2117 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN },
2118 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2123 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1)
2125 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering1");
2126 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2129 { 96, 300, 0.0, dcp::VAlign::TOP, "This" },
2130 { 96, 300, 0.1, dcp::VAlign::TOP, "is" },
2131 { 96, 300, 0.2, dcp::VAlign::TOP, "fine" },
2133 check_verify_result (
2136 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2141 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2)
2143 auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering2");
2144 auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> (
2147 { 96, 300, 0.2, dcp::VAlign::BOTTOM, "This" },
2148 { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" },
2149 { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" },
2151 check_verify_result (
2154 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2159 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3)
2161 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering3");
2162 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering3.xml");
2163 check_verify_result (
2166 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING },
2167 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2172 BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4)
2174 auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4");
2175 auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml");
2176 check_verify_result (
2179 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2185 BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate)
2187 path const dir("build/test/verify_invalid_sound_frame_rate");
2188 prepare_directory (dir);
2190 auto picture = simple_picture (dir, "foo");
2191 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
2192 auto reel = make_shared<dcp::Reel>();
2193 reel->add (reel_picture);
2194 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000, boost::none);
2195 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
2196 reel->add (reel_sound);
2197 reel->add (simple_markers());
2198 auto cpl = make_shared<dcp::CPL>("hello", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2200 auto dcp = make_shared<dcp::DCP>(dir);
2202 dcp->set_annotation_text("hello");
2205 check_verify_result (
2208 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_SOUND_FRAME_RATE, string("96000"), canonical(dir / "audiofoo.mxf") },
2209 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() },
2214 BOOST_AUTO_TEST_CASE (verify_missing_cpl_annotation_text)
2216 path const dir("build/test/verify_missing_cpl_annotation_text");
2217 auto dcp = make_simple (dir);
2218 dcp->set_annotation_text("A Test DCP");
2221 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2223 auto const cpl = dcp->cpls()[0];
2226 BOOST_REQUIRE (cpl->file());
2227 Editor e(cpl->file().get());
2228 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
2231 check_verify_result (
2234 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) },
2235 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) }
2240 BOOST_AUTO_TEST_CASE (verify_mismatched_cpl_annotation_text)
2242 path const dir("build/test/verify_mismatched_cpl_annotation_text");
2243 auto dcp = make_simple (dir);
2244 dcp->set_annotation_text("A Test DCP");
2247 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2248 auto const cpl = dcp->cpls()[0];
2251 BOOST_REQUIRE (cpl->file());
2252 Editor e(cpl->file().get());
2253 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
2256 check_verify_result (
2259 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISMATCHED_CPL_ANNOTATION_TEXT, cpl->id(), canonical(cpl->file().get()) },
2260 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), canonical(cpl->file().get()) }
2265 BOOST_AUTO_TEST_CASE (verify_mismatched_asset_duration)
2267 path const dir("build/test/verify_mismatched_asset_duration");
2268 prepare_directory (dir);
2269 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
2270 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2272 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
2273 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
2275 auto reel = make_shared<dcp::Reel>(
2276 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
2277 make_shared<dcp::ReelSoundAsset>(ms, 0)
2280 reel->add (simple_markers());
2284 dcp->set_annotation_text("A Test DCP");
2287 check_verify_result (
2290 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_ASSET_DURATION },
2291 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), canonical(cpl->file().get()) }
2298 shared_ptr<dcp::CPL>
2299 verify_subtitles_must_be_in_all_reels_check (path dir, bool add_to_reel1, bool add_to_reel2)
2301 prepare_directory (dir);
2302 auto dcp = make_shared<dcp::DCP>(dir);
2303 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2305 auto constexpr reel_length = 192;
2307 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2308 subs->set_language (dcp::LanguageTag("de-DE"));
2309 subs->set_start_time (dcp::Time());
2310 subs->add (simple_subtitle());
2311 subs->write (dir / "subs.mxf");
2312 auto reel_subs = make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0);
2314 auto reel1 = make_shared<dcp::Reel>(
2315 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2316 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2320 reel1->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2323 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2324 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2325 reel1->add (markers1);
2329 auto reel2 = make_shared<dcp::Reel>(
2330 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2331 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2335 reel2->add (make_shared<dcp::ReelSMPTESubtitleAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2338 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2339 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
2340 reel2->add (markers2);
2345 dcp->set_annotation_text("A Test DCP");
2352 BOOST_AUTO_TEST_CASE (verify_missing_main_subtitle_from_some_reels)
2355 path dir ("build/test/missing_main_subtitle_from_some_reels");
2356 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, false);
2357 check_verify_result (
2360 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS },
2361 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2367 path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
2368 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, true, true);
2369 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2373 path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
2374 auto cpl = verify_subtitles_must_be_in_all_reels_check (dir, false, false);
2375 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2381 shared_ptr<dcp::CPL>
2382 verify_closed_captions_must_be_in_all_reels_check (path dir, int caps_in_reel1, int caps_in_reel2)
2384 prepare_directory (dir);
2385 auto dcp = make_shared<dcp::DCP>(dir);
2386 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2388 auto constexpr reel_length = 192;
2390 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2391 subs->set_language (dcp::LanguageTag("de-DE"));
2392 subs->set_start_time (dcp::Time());
2393 subs->add (simple_subtitle());
2394 subs->write (dir / "subs.mxf");
2396 auto reel1 = make_shared<dcp::Reel>(
2397 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2398 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2401 for (int i = 0; i < caps_in_reel1; ++i) {
2402 reel1->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2405 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2406 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2407 reel1->add (markers1);
2411 auto reel2 = make_shared<dcp::Reel>(
2412 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2413 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2416 for (int i = 0; i < caps_in_reel2; ++i) {
2417 reel2->add (make_shared<dcp::ReelSMPTEClosedCaptionAsset>(subs, dcp::Fraction(24, 1), reel_length, 0));
2420 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), reel_length);
2421 markers2->set (dcp::Marker::LFOC, dcp::Time(reel_length - 1, 24, 24));
2422 reel2->add (markers2);
2427 dcp->set_annotation_text("A Test DCP");
2434 BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_asset_counts)
2437 path dir ("build/test/mismatched_closed_caption_asset_counts");
2438 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2439 check_verify_result (
2442 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS },
2443 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2448 path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2449 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2450 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2454 path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2455 auto cpl = verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2456 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }});
2463 verify_text_entry_point_check (path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2465 prepare_directory (dir);
2466 auto dcp = make_shared<dcp::DCP>(dir);
2467 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2469 auto constexpr reel_length = 192;
2471 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2472 subs->set_language (dcp::LanguageTag("de-DE"));
2473 subs->set_start_time (dcp::Time());
2474 subs->add (simple_subtitle());
2475 subs->write (dir / "subs.mxf");
2476 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), reel_length, 0);
2479 auto reel = make_shared<dcp::Reel>(
2480 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", reel_length), 0),
2481 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", reel_length), 0)
2484 reel->add (reel_text);
2486 reel->add (simple_markers(reel_length));
2491 dcp->set_annotation_text("A Test DCP");
2494 check_verify_result (
2497 { dcp::VerificationNote::Type::BV21_ERROR, code, subs->id() },
2498 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
2503 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2505 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
2506 "build/test/verify_subtitle_entry_point_must_be_present",
2507 dcp::VerificationNote::Code::MISSING_SUBTITLE_ENTRY_POINT,
2508 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
2509 asset->unset_entry_point ();
2513 verify_text_entry_point_check<dcp::ReelSMPTESubtitleAsset> (
2514 "build/test/verify_subtitle_entry_point_must_be_zero",
2515 dcp::VerificationNote::Code::INCORRECT_SUBTITLE_ENTRY_POINT,
2516 [](shared_ptr<dcp::ReelSMPTESubtitleAsset> asset) {
2517 asset->set_entry_point (4);
2521 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
2522 "build/test/verify_closed_caption_entry_point_must_be_present",
2523 dcp::VerificationNote::Code::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2524 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
2525 asset->unset_entry_point ();
2529 verify_text_entry_point_check<dcp::ReelSMPTEClosedCaptionAsset> (
2530 "build/test/verify_closed_caption_entry_point_must_be_zero",
2531 dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ENTRY_POINT,
2532 [](shared_ptr<dcp::ReelSMPTEClosedCaptionAsset> asset) {
2533 asset->set_entry_point (9);
2539 BOOST_AUTO_TEST_CASE (verify_missing_hash)
2543 path const dir("build/test/verify_missing_hash");
2544 auto dcp = make_simple (dir);
2545 dcp->set_annotation_text("A Test DCP");
2548 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2549 auto const cpl = dcp->cpls()[0];
2550 BOOST_REQUIRE_EQUAL (cpl->reels().size(), 1U);
2551 BOOST_REQUIRE (cpl->reels()[0]->main_picture());
2552 auto asset_id = cpl->reels()[0]->main_picture()->id();
2555 BOOST_REQUIRE (cpl->file());
2556 Editor e(cpl->file().get());
2557 e.delete_first_line_containing("<Hash>");
2560 check_verify_result (
2563 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2564 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_HASH, asset_id }
2571 verify_markers_test (
2573 vector<pair<dcp::Marker, dcp::Time>> markers,
2574 vector<dcp::VerificationNote> test_notes
2577 auto dcp = make_simple (dir);
2578 dcp->cpls()[0]->set_content_kind (dcp::ContentKind::FEATURE);
2579 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24);
2580 for (auto const& i: markers) {
2581 markers_asset->set (i.first, i.second);
2583 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2584 dcp->set_annotation_text("A Test DCP");
2587 check_verify_result ({dir}, test_notes);
2591 BOOST_AUTO_TEST_CASE (verify_markers)
2593 verify_markers_test (
2594 "build/test/verify_markers_all_correct",
2596 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2597 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2598 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2599 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2604 verify_markers_test (
2605 "build/test/verify_markers_missing_ffec",
2607 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2608 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2609 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2612 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE }
2615 verify_markers_test (
2616 "build/test/verify_markers_missing_ffmc",
2618 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2619 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2620 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2623 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE }
2626 verify_markers_test (
2627 "build/test/verify_markers_missing_ffoc",
2629 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2630 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2631 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2634 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC}
2637 verify_markers_test (
2638 "build/test/verify_markers_missing_lfoc",
2640 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2641 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2642 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2645 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC }
2648 verify_markers_test (
2649 "build/test/verify_markers_incorrect_ffoc",
2651 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2652 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2653 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2654 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2657 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_FFOC, string("3") }
2660 verify_markers_test (
2661 "build/test/verify_markers_incorrect_lfoc",
2663 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2664 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2665 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2666 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2669 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INCORRECT_LFOC, string("18") }
2674 BOOST_AUTO_TEST_CASE (verify_missing_cpl_metadata_version_number)
2676 path dir = "build/test/verify_missing_cpl_metadata_version_number";
2677 prepare_directory (dir);
2678 auto dcp = make_simple (dir);
2679 auto cpl = dcp->cpls()[0];
2680 cpl->unset_version_number();
2681 dcp->set_annotation_text("A Test DCP");
2684 check_verify_result ({dir}, {{ dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA_VERSION_NUMBER, cpl->id(), cpl->file().get() }});
2688 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata1)
2690 path dir = "build/test/verify_missing_extension_metadata1";
2691 auto dcp = make_simple (dir);
2692 dcp->set_annotation_text("A Test DCP");
2695 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2696 auto cpl = dcp->cpls()[0];
2699 Editor e (cpl->file().get());
2700 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2703 check_verify_result (
2706 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2707 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() }
2712 BOOST_AUTO_TEST_CASE (verify_missing_extension_metadata2)
2714 path dir = "build/test/verify_missing_extension_metadata2";
2715 auto dcp = make_simple (dir);
2716 dcp->set_annotation_text("A Test DCP");
2719 auto cpl = dcp->cpls()[0];
2722 Editor e (cpl->file().get());
2723 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2726 check_verify_result (
2729 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2730 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_EXTENSION_METADATA, cpl->id(), cpl->file().get() }
2735 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata3)
2737 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata3";
2738 auto dcp = make_simple (dir);
2739 dcp->set_annotation_text("A Test DCP");
2742 auto const cpl = dcp->cpls()[0];
2745 Editor e (cpl->file().get());
2746 e.replace ("<meta:Name>A", "<meta:NameX>A");
2747 e.replace ("n</meta:Name>", "n</meta:NameX>");
2750 check_verify_result (
2753 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:NameX'"), cpl->file().get(), 70 },
2754 { 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 },
2755 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2760 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata1)
2762 path dir = "build/test/verify_invalid_extension_metadata1";
2763 auto dcp = make_simple (dir);
2764 dcp->set_annotation_text("A Test DCP");
2767 auto cpl = dcp->cpls()[0];
2770 Editor e (cpl->file().get());
2771 e.replace ("Application", "Fred");
2774 check_verify_result (
2777 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2778 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> should be 'Application'"), cpl->file().get() },
2783 BOOST_AUTO_TEST_CASE (verify_invalid_extension_metadata2)
2785 path dir = "build/test/verify_invalid_extension_metadata2";
2786 auto dcp = make_simple (dir);
2787 dcp->set_annotation_text("A Test DCP");
2790 auto cpl = dcp->cpls()[0];
2793 Editor e (cpl->file().get());
2794 e.replace ("DCP Constraints Profile", "Fred");
2797 check_verify_result (
2800 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2801 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'"), cpl->file().get() },
2806 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata6)
2808 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata6";
2809 auto dcp = make_simple (dir);
2810 dcp->set_annotation_text("A Test DCP");
2813 auto const cpl = dcp->cpls()[0];
2816 Editor e (cpl->file().get());
2817 e.replace ("<meta:Value>", "<meta:ValueX>");
2818 e.replace ("</meta:Value>", "</meta:ValueX>");
2821 check_verify_result (
2824 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:ValueX'"), cpl->file().get(), 74 },
2825 { 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 },
2826 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2831 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata7)
2833 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata7";
2834 auto dcp = make_simple (dir);
2835 dcp->set_annotation_text("A Test DCP");
2838 auto const cpl = dcp->cpls()[0];
2841 Editor e (cpl->file().get());
2842 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2845 check_verify_result (
2848 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2849 { 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() },
2854 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata8)
2856 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata8";
2857 auto dcp = make_simple (dir);
2858 dcp->set_annotation_text("A Test DCP");
2861 auto const cpl = dcp->cpls()[0];
2864 Editor e (cpl->file().get());
2865 e.replace ("<meta:Property>", "<meta:PropertyX>");
2866 e.replace ("</meta:Property>", "</meta:PropertyX>");
2869 check_verify_result (
2872 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyX'"), cpl->file().get(), 72 },
2873 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("element 'meta:PropertyX' is not allowed for content model '(Property+)'"), cpl->file().get(), 76 },
2874 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2879 BOOST_AUTO_TEST_CASE (verify_invalid_xml_cpl_extension_metadata9)
2881 path dir = "build/test/verify_invalid_xml_cpl_extension_metadata9";
2882 auto dcp = make_simple (dir);
2883 dcp->set_annotation_text("A Test DCP");
2886 auto const cpl = dcp->cpls()[0];
2889 Editor e (cpl->file().get());
2890 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2891 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2894 check_verify_result (
2897 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_XML, string("no declaration found for element 'meta:PropertyListX'"), cpl->file().get(), 71 },
2898 { 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 },
2899 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl->id(), cpl->file().get() },
2905 BOOST_AUTO_TEST_CASE (verify_unsigned_cpl_with_encrypted_content)
2907 path dir = "build/test/verify_unsigned_cpl_with_encrypted_content";
2908 prepare_directory (dir);
2909 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
2910 copy_file (i.path(), dir / i.path().filename());
2913 path const pkl = dir / ( "pkl_" + encryption_test_pkl_id + ".xml" );
2914 path const cpl = dir / ( "cpl_" + encryption_test_cpl_id + ".xml");
2918 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2921 check_verify_result (
2924 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, encryption_test_cpl_id, canonical(cpl) },
2925 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id, canonical(pkl), },
2926 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE },
2927 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE },
2928 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC },
2929 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC },
2930 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id, canonical(cpl) },
2931 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT, encryption_test_cpl_id, canonical(cpl) }
2936 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_encrypted_content)
2938 path dir = "build/test/unsigned_pkl_with_encrypted_content";
2939 prepare_directory (dir);
2940 for (auto i: directory_iterator("test/ref/DCP/encryption_test")) {
2941 copy_file (i.path(), dir / i.path().filename());
2944 path const cpl = dir / ("cpl_" + encryption_test_cpl_id + ".xml");
2945 path const pkl = dir / ("pkl_" + encryption_test_pkl_id + ".xml");
2948 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2951 check_verify_result (
2954 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, encryption_test_pkl_id, canonical(pkl) },
2955 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFEC_IN_FEATURE },
2956 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_FFMC_IN_FEATURE },
2957 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_FFOC },
2958 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::MISSING_LFOC },
2959 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, encryption_test_cpl_id, canonical(cpl) },
2960 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT, encryption_test_pkl_id, canonical(pkl) },
2965 BOOST_AUTO_TEST_CASE (verify_unsigned_pkl_with_unencrypted_content)
2967 path dir = "build/test/verify_unsigned_pkl_with_unencrypted_content";
2968 prepare_directory (dir);
2969 for (auto i: directory_iterator("test/ref/DCP/dcp_test1")) {
2970 copy_file (i.path(), dir / i.path().filename());
2974 Editor e (dir / dcp_test1_pkl);
2975 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2978 check_verify_result ({dir}, {});
2982 BOOST_AUTO_TEST_CASE (verify_partially_encrypted)
2984 path dir ("build/test/verify_must_not_be_partially_encrypted");
2985 prepare_directory (dir);
2989 auto signer = make_shared<dcp::CertificateChain>();
2990 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
2991 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
2992 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
2993 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
2995 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::ContentKind::TRAILER, dcp::Standard::SMPTE);
2999 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::Standard::SMPTE);
3002 auto writer = mp->start_write (dir / "video.mxf", false);
3003 dcp::ArrayData j2c ("test/data/flat_red.j2c");
3004 for (int i = 0; i < 24; ++i) {
3005 writer->write (j2c.data(), j2c.size());
3007 writer->finalize ();
3009 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
3011 auto reel = make_shared<dcp::Reel>(
3012 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
3013 make_shared<dcp::ReelSoundAsset>(ms, 0)
3016 reel->add (simple_markers());
3020 cpl->set_content_version (
3021 {"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"}
3023 cpl->set_annotation_text ("A Test DCP");
3024 cpl->set_issuer ("OpenDCP 0.0.25");
3025 cpl->set_creator ("OpenDCP 0.0.25");
3026 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
3027 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
3028 cpl->set_main_sound_sample_rate (48000);
3029 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
3030 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
3031 cpl->set_version_number (1);
3035 d.set_issuer("OpenDCP 0.0.25");
3036 d.set_creator("OpenDCP 0.0.25");
3037 d.set_issue_date("2012-07-17T04:45:18+00:00");
3038 d.set_annotation_text("A Test DCP");
3039 d.write_xml(signer);
3041 check_verify_result (
3044 {dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::PARTIALLY_ENCRYPTED},
3049 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_2k)
3051 vector<dcp::VerificationNote> notes;
3052 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"));
3053 auto reader = picture.start_read ();
3054 auto frame = reader->get_frame (0);
3055 verify_j2k (frame, notes);
3056 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3060 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_4k)
3062 vector<dcp::VerificationNote> notes;
3063 dcp::MonoPictureAsset picture (find_file(private_test / "data" / "sul", "TLR"));
3064 auto reader = picture.start_read ();
3065 auto frame = reader->get_frame (0);
3066 verify_j2k (frame, notes);
3067 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3071 BOOST_AUTO_TEST_CASE (verify_jpeg2000_codestream_libdcp)
3073 boost::filesystem::path dir = "build/test/verify_jpeg2000_codestream_libdcp";
3074 prepare_directory (dir);
3075 auto dcp = make_simple (dir);
3077 vector<dcp::VerificationNote> notes;
3078 dcp::MonoPictureAsset picture (find_file(dir, "video"));
3079 auto reader = picture.start_read ();
3080 auto frame = reader->get_frame (0);
3081 verify_j2k (frame, notes);
3082 BOOST_REQUIRE_EQUAL (notes.size(), 0U);
3086 /** Check that ResourceID and the XML ID being different is spotted */
3087 BOOST_AUTO_TEST_CASE (verify_mismatched_subtitle_resource_id)
3089 boost::filesystem::path const dir = "build/test/verify_mismatched_subtitle_resource_id";
3090 prepare_directory (dir);
3092 ASDCP::WriterInfo writer_info;
3093 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
3096 auto mxf_id = dcp::make_uuid ();
3097 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
3098 BOOST_REQUIRE (c == Kumu::UUID_Length);
3100 auto resource_id = dcp::make_uuid ();
3101 ASDCP::TimedText::TimedTextDescriptor descriptor;
3102 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
3103 DCP_ASSERT (c == Kumu::UUID_Length);
3105 auto xml_id = dcp::make_uuid ();
3106 ASDCP::TimedText::MXFWriter writer;
3107 auto subs_mxf = dir / "subs.mxf";
3108 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
3109 BOOST_REQUIRE (ASDCP_SUCCESS(r));
3110 writer.WriteTimedTextResource (dcp::String::compose(
3111 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
3112 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
3113 "<Id>urn:uuid:%1</Id>"
3114 "<ContentTitleText>Content</ContentTitleText>"
3115 "<AnnotationText>Annotation</AnnotationText>"
3116 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
3117 "<ReelNumber>1</ReelNumber>"
3118 "<Language>en-US</Language>"
3119 "<EditRate>25 1</EditRate>"
3120 "<TimeCodeRate>25</TimeCodeRate>"
3121 "<StartTime>00:00:00:00</StartTime>"
3123 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
3124 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
3125 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
3134 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
3135 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
3137 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
3139 check_verify_result (
3142 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) },
3143 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_RESOURCE_ID },
3144 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
3145 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
3150 /** Check that ResourceID and the MXF ID being the same is spotted */
3151 BOOST_AUTO_TEST_CASE (verify_incorrect_timed_text_id)
3153 boost::filesystem::path const dir = "build/test/verify_incorrect_timed_text_id";
3154 prepare_directory (dir);
3156 ASDCP::WriterInfo writer_info;
3157 writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE;
3160 auto mxf_id = dcp::make_uuid ();
3161 Kumu::hex2bin (mxf_id.c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c);
3162 BOOST_REQUIRE (c == Kumu::UUID_Length);
3164 auto resource_id = mxf_id;
3165 ASDCP::TimedText::TimedTextDescriptor descriptor;
3166 Kumu::hex2bin (resource_id.c_str(), descriptor.AssetID, Kumu::UUID_Length, &c);
3167 DCP_ASSERT (c == Kumu::UUID_Length);
3169 auto xml_id = resource_id;
3170 ASDCP::TimedText::MXFWriter writer;
3171 auto subs_mxf = dir / "subs.mxf";
3172 auto r = writer.OpenWrite(subs_mxf.string().c_str(), writer_info, descriptor, 4096);
3173 BOOST_REQUIRE (ASDCP_SUCCESS(r));
3174 writer.WriteTimedTextResource (dcp::String::compose(
3175 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
3176 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
3177 "<Id>urn:uuid:%1</Id>"
3178 "<ContentTitleText>Content</ContentTitleText>"
3179 "<AnnotationText>Annotation</AnnotationText>"
3180 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
3181 "<ReelNumber>1</ReelNumber>"
3182 "<Language>en-US</Language>"
3183 "<EditRate>25 1</EditRate>"
3184 "<TimeCodeRate>25</TimeCodeRate>"
3185 "<StartTime>00:00:00:00</StartTime>"
3187 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
3188 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
3189 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
3198 auto subs_asset = make_shared<dcp::SMPTESubtitleAsset>(subs_mxf);
3199 auto subs_reel = make_shared<dcp::ReelSMPTESubtitleAsset>(subs_asset, dcp::Fraction(24, 1), 240, 0);
3201 auto cpl = write_dcp_with_single_asset (dir, subs_reel);
3203 check_verify_result (
3206 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_TIMED_TEXT_DURATION , "240 0", boost::filesystem::canonical(subs_mxf) },
3207 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::INCORRECT_TIMED_TEXT_ASSET_ID },
3208 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::INVALID_SUBTITLE_FIRST_TEXT_TIME },
3209 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() }
3214 /** Check a DCP with a 3D asset marked as 2D */
3215 BOOST_AUTO_TEST_CASE (verify_threed_marked_as_twod)
3217 check_verify_result (
3218 { private_test / "data" / "xm" },
3221 dcp::VerificationNote::Type::WARNING,
3222 dcp::VerificationNote::Code::THREED_ASSET_MARKED_AS_TWOD, boost::filesystem::canonical(find_file(private_test / "data" / "xm", "j2c"))
3225 dcp::VerificationNote::Type::BV21_ERROR,
3226 dcp::VerificationNote::Code::INVALID_STANDARD
3233 BOOST_AUTO_TEST_CASE (verify_unexpected_things_in_main_markers)
3235 path dir = "build/test/verify_unexpected_things_in_main_markers";
3236 prepare_directory (dir);
3237 auto dcp = make_simple (dir, 1, 24);
3238 dcp->set_annotation_text("A Test DCP");
3242 Editor e (find_cpl(dir));
3244 " <IntrinsicDuration>24</IntrinsicDuration>",
3245 "<EntryPoint>0</EntryPoint><Duration>24</Duration>"
3249 dcp::CPL cpl (find_cpl(dir));
3251 check_verify_result (
3254 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3255 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_ENTRY_POINT },
3256 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::UNEXPECTED_DURATION },
3261 BOOST_AUTO_TEST_CASE(verify_invalid_content_kind)
3263 path dir = "build/test/verify_invalid_content_kind";
3264 prepare_directory (dir);
3265 auto dcp = make_simple (dir, 1, 24);
3266 dcp->set_annotation_text("A Test DCP");
3270 Editor e(find_cpl(dir));
3271 e.replace("trailer", "trip");
3274 dcp::CPL cpl (find_cpl(dir));
3276 check_verify_result (
3279 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3280 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_CONTENT_KIND, string("trip") }
3286 BOOST_AUTO_TEST_CASE(verify_valid_content_kind)
3288 path dir = "build/test/verify_valid_content_kind";
3289 prepare_directory (dir);
3290 auto dcp = make_simple (dir, 1, 24);
3291 dcp->set_annotation_text("A Test DCP");
3295 Editor e(find_cpl(dir));
3296 e.replace("<ContentKind>trailer</ContentKind>", "<ContentKind scope=\"http://bobs.contents/\">trip</ContentKind>");
3299 dcp::CPL cpl (find_cpl(dir));
3301 check_verify_result (
3304 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3310 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_1)
3312 path dir = "build/test/verify_invalid_main_picture_active_area_1";
3313 prepare_directory(dir);
3314 auto dcp = make_simple(dir, 1, 24);
3317 auto constexpr area = "<meta:MainPictureActiveArea>";
3320 Editor e(find_cpl(dir));
3321 e.delete_lines_after(area, 2);
3322 e.insert(area, "<meta:Height>4080</meta:Height>");
3323 e.insert(area, "<meta:Width>1997</meta:Width>");
3326 dcp::PKL pkl(find_pkl(dir));
3327 dcp::CPL cpl(find_cpl(dir));
3329 check_verify_result(
3332 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3333 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3334 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "width 1997 is not a multiple of 2", canonical(find_cpl(dir)) },
3335 { 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)) },
3340 BOOST_AUTO_TEST_CASE(verify_invalid_main_picture_active_area_2)
3342 path dir = "build/test/verify_invalid_main_picture_active_area_2";
3343 prepare_directory(dir);
3344 auto dcp = make_simple(dir, 1, 24);
3347 auto constexpr area = "<meta:MainPictureActiveArea>";
3350 Editor e(find_cpl(dir));
3351 e.delete_lines_after(area, 2);
3352 e.insert(area, "<meta:Height>5125</meta:Height>");
3353 e.insert(area, "<meta:Width>9900</meta:Width>");
3356 dcp::PKL pkl(find_pkl(dir));
3357 dcp::CPL cpl(find_cpl(dir));
3359 check_verify_result(
3362 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CPL_HASHES, cpl.id(), canonical(find_cpl(dir)) },
3363 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3364 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INVALID_MAIN_PICTURE_ACTIVE_AREA, "height 5125 is not a multiple of 2", canonical(find_cpl(dir)) },
3365 { 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)) },
3366 { 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)) },
3371 BOOST_AUTO_TEST_CASE(verify_duplicate_pkl_asset_ids)
3375 path dir = "build/test/verify_duplicate_pkl_asset_ids";
3376 prepare_directory(dir);
3377 auto dcp = make_simple(dir, 1, 24);
3381 Editor e(find_pkl(dir));
3382 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:6affb8ee-0020-4dff-a53c-17652f6358ab");
3385 dcp::PKL pkl(find_pkl(dir));
3387 check_verify_result(
3390 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_PKL, pkl.id(), canonical(find_pkl(dir)) },
3395 BOOST_AUTO_TEST_CASE(verify_duplicate_assetmap_asset_ids)
3399 path dir = "build/test/verify_duplicate_assetmap_asset_ids";
3400 prepare_directory(dir);
3401 auto dcp = make_simple(dir, 1, 24);
3405 Editor e(find_asset_map(dir));
3406 e.replace("urn:uuid:5407b210-4441-4e97-8b16-8bdc7c12da54", "urn:uuid:97f0f352-5b77-48ee-a558-9df37717f4fa");
3409 dcp::PKL pkl(find_pkl(dir));
3410 dcp::AssetMap asset_map(find_asset_map(dir));
3412 check_verify_result(
3415 { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISMATCHED_PKL_ANNOTATION_TEXT_WITH_CPL, pkl.id(), canonical(find_pkl(dir)), },
3416 { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::DUPLICATE_ASSET_ID_IN_ASSETMAP, asset_map.id(), canonical(find_asset_map(dir)) },
3417 { dcp::VerificationNote::Type::WARNING, dcp::VerificationNote::Code::EXTERNAL_ASSET, string("5407b210-4441-4e97-8b16-8bdc7c12da54") },