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.
38 #include "reel_mono_picture_asset.h"
39 #include "reel_sound_asset.h"
42 #include "openjpeg_image.h"
43 #include "mono_picture_asset.h"
44 #include "stereo_picture_asset.h"
45 #include "mono_picture_asset_writer.h"
46 #include "interop_subtitle_asset.h"
47 #include "smpte_subtitle_asset.h"
48 #include "reel_closed_caption_asset.h"
49 #include "reel_stereo_picture_asset.h"
50 #include "reel_subtitle_asset.h"
51 #include "reel_markers_asset.h"
52 #include "compose.hpp"
54 #include <boost/test/unit_test.hpp>
55 #include <boost/foreach.hpp>
56 #include <boost/algorithm/string.hpp>
65 using std::make_shared;
66 using boost::optional;
67 using std::shared_ptr;
70 static list<pair<string, optional<boost::filesystem::path>>> stages;
71 static string const dcp_test1_pkl = "pkl_2b9b857f-ab4a-440e-a313-1ace0f1cfc95.xml";
72 static string const dcp_test1_cpl = "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
75 stage (string s, optional<boost::filesystem::path> p)
77 stages.push_back (make_pair (s, p));
87 prepare_directory (boost::filesystem::path path)
89 using namespace boost::filesystem;
91 create_directories (path);
95 static vector<boost::filesystem::path>
96 setup (int reference_number, int verify_test_number)
98 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
99 for (auto i: boost::filesystem::directory_iterator(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number))) {
100 boost::filesystem::copy_file (i.path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i.path().filename());
103 return { dcp::String::compose("build/test/verify_test%1", verify_test_number) };
110 write_dcp_with_single_asset (boost::filesystem::path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::SMPTE)
112 auto reel = make_shared<dcp::Reel>();
113 reel->add (reel_asset);
114 reel->add (simple_markers());
116 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
118 auto dcp = make_shared<dcp::DCP>(dir);
122 dcp::String::compose("libdcp %1", dcp::version),
123 dcp::String::compose("libdcp %1", dcp::version),
124 dcp::LocalTime().as_string(),
130 /** Class that can alter a file by searching and replacing strings within it.
131 * On destruction modifies the file whose name was given to the constructor.
136 Editor (boost::filesystem::path path)
139 _content = dcp::file_to_string (_path);
144 auto f = fopen(_path.string().c_str(), "w");
146 fwrite (_content.c_str(), _content.length(), 1, f);
150 void replace (string a, string b)
152 auto old_content = _content;
153 boost::algorithm::replace_all (_content, a, b);
154 BOOST_REQUIRE (_content != old_content);
157 void delete_lines (string from, string to)
159 vector<string> lines;
160 boost::algorithm::split (lines, _content, boost::is_any_of("\r\n"), boost::token_compress_on);
161 bool deleting = false;
162 auto old_content = _content;
164 for (auto i: lines) {
165 if (i.find(from) != string::npos) {
169 _content += i + "\n";
171 if (deleting && i.find(to) != string::npos) {
175 BOOST_REQUIRE (_content != old_content);
179 boost::filesystem::path _path;
180 std::string _content;
186 dump_notes (vector<dcp::VerificationNote> const & notes)
188 for (auto i: notes) {
189 std::cout << dcp::note_to_string(i) << "\n";
196 check_verify_result (vector<boost::filesystem::path> dir, vector<dcp::VerificationNote> test_notes)
198 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
200 BOOST_REQUIRE_EQUAL (notes.size(), test_notes.size());
206 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
208 auto directories = setup (1, n);
212 e.replace (from, to);
215 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
217 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
218 auto i = notes.begin();
219 auto j = codes.begin();
220 while (i != notes.end()) {
221 BOOST_CHECK_EQUAL (i->code(), *j);
228 /* Check DCP as-is (should be OK) */
229 BOOST_AUTO_TEST_CASE (verify_test1)
232 auto directories = setup (1, 1);
233 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
235 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test1" / dcp_test1_cpl;
236 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test1" / dcp_test1_pkl;
237 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
239 auto st = stages.begin();
240 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
241 BOOST_REQUIRE (st->second);
242 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
244 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
245 BOOST_REQUIRE (st->second);
246 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
248 BOOST_CHECK_EQUAL (st->first, "Checking reel");
249 BOOST_REQUIRE (!st->second);
251 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
252 BOOST_REQUIRE (st->second);
253 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
255 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
256 BOOST_REQUIRE (st->second);
257 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
259 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
260 BOOST_REQUIRE (st->second);
261 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
263 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
264 BOOST_REQUIRE (st->second);
265 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
267 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
268 BOOST_REQUIRE (st->second);
269 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
271 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
272 BOOST_REQUIRE (st->second);
273 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
275 BOOST_REQUIRE (st == stages.end());
277 BOOST_CHECK_EQUAL (notes.size(), 0);
280 /* Corrupt the MXFs and check that this is spotted */
281 BOOST_AUTO_TEST_CASE (verify_test2)
283 auto directories = setup (1, 2);
285 auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
287 fseek (mod, 4096, SEEK_SET);
289 fwrite (&x, sizeof(x), 1, mod);
292 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
294 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
295 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
298 dcp::ASDCPErrorSuspender sus;
299 check_verify_result (
302 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
303 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
307 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
308 BOOST_AUTO_TEST_CASE (verify_test3)
310 auto directories = setup (1, 3);
313 Editor e (boost::filesystem::path("build") / "test" / "verify_test3" / dcp_test1_pkl);
314 e.replace ("<Hash>", "<Hash>x");
317 check_verify_result (
320 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
321 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER },
322 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER },
323 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
324 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
325 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
329 /* Corrupt the ContentKind in the CPL */
330 BOOST_AUTO_TEST_CASE (verify_test4)
332 auto directories = setup (1, 4);
335 Editor e (boost::filesystem::path("build") / "test" / "verify_test4" / dcp_test1_cpl);
336 e.replace ("<ContentKind>", "<ContentKind>x");
339 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
341 BOOST_REQUIRE_EQUAL (notes.size(), 1);
342 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
343 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xtrailer'");
347 boost::filesystem::path
350 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_cpl);
354 boost::filesystem::path
357 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_pkl);
361 boost::filesystem::path
364 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
369 BOOST_AUTO_TEST_CASE (verify_test5)
371 check_verify_result_after_replace (
373 "<FrameRate>24 1", "<FrameRate>99 1",
374 { dcp::VerificationNote::CPL_HASH_INCORRECT,
375 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
380 BOOST_AUTO_TEST_CASE (verify_test6)
382 auto directories = setup (1, 6);
384 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
385 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
389 boost::filesystem::path
392 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
395 /* Empty asset filename in ASSETMAP */
396 BOOST_AUTO_TEST_CASE (verify_test7)
398 check_verify_result_after_replace (
400 "<Path>video.mxf</Path>", "<Path></Path>",
401 { dcp::VerificationNote::EMPTY_ASSET_PATH }
405 /* Mismatched standard */
406 BOOST_AUTO_TEST_CASE (verify_test8)
408 check_verify_result_after_replace (
410 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
411 { dcp::VerificationNote::MISMATCHED_STANDARD,
412 dcp::VerificationNote::XML_VALIDATION_ERROR,
413 dcp::VerificationNote::XML_VALIDATION_ERROR,
414 dcp::VerificationNote::XML_VALIDATION_ERROR,
415 dcp::VerificationNote::XML_VALIDATION_ERROR,
416 dcp::VerificationNote::XML_VALIDATION_ERROR,
417 dcp::VerificationNote::CPL_HASH_INCORRECT }
421 /* Badly formatted <Id> in CPL */
422 BOOST_AUTO_TEST_CASE (verify_test9)
424 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
425 check_verify_result_after_replace (
427 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
428 { dcp::VerificationNote::XML_VALIDATION_ERROR }
432 /* Badly formatted <IssueDate> in CPL */
433 BOOST_AUTO_TEST_CASE (verify_test10)
435 check_verify_result_after_replace (
437 "<IssueDate>", "<IssueDate>x",
438 { dcp::VerificationNote::XML_VALIDATION_ERROR,
439 dcp::VerificationNote::CPL_HASH_INCORRECT }
443 /* Badly-formatted <Id> in PKL */
444 BOOST_AUTO_TEST_CASE (verify_test11)
446 check_verify_result_after_replace (
448 "<Id>urn:uuid:2b9", "<Id>urn:uuid:xb9",
449 { dcp::VerificationNote::XML_VALIDATION_ERROR }
453 /* Badly-formatted <Id> in ASSETMAP */
454 BOOST_AUTO_TEST_CASE (verify_test12)
456 check_verify_result_after_replace (
458 "<Id>urn:uuid:07e", "<Id>urn:uuid:x7e",
459 { dcp::VerificationNote::XML_VALIDATION_ERROR }
463 /* Basic test of an Interop DCP */
464 BOOST_AUTO_TEST_CASE (verify_test13)
467 auto directories = setup (3, 13);
468 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
470 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
471 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
472 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
474 auto st = stages.begin();
475 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
476 BOOST_REQUIRE (st->second);
477 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
479 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
480 BOOST_REQUIRE (st->second);
481 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
483 BOOST_CHECK_EQUAL (st->first, "Checking reel");
484 BOOST_REQUIRE (!st->second);
486 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
487 BOOST_REQUIRE (st->second);
488 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
490 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
491 BOOST_REQUIRE (st->second);
492 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
494 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
495 BOOST_REQUIRE (st->second);
496 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
498 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
499 BOOST_REQUIRE (st->second);
500 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
502 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
503 BOOST_REQUIRE (st->second);
504 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
506 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
507 BOOST_REQUIRE (st->second);
508 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
510 BOOST_REQUIRE (st == stages.end());
512 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
513 auto i = notes.begin ();
514 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
515 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
518 /* DCP with a short asset */
519 BOOST_AUTO_TEST_CASE (verify_test14)
521 auto directories = setup (8, 14);
522 check_verify_result (
525 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
526 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
527 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
528 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
529 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
536 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
538 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
539 boost::filesystem::create_directories (dir);
540 auto writer = asset->start_write (dir / "pic.mxf", true);
541 for (int i = 0; i < 24; ++i) {
542 writer->write (frame.data(), frame.size());
546 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
547 write_dcp_with_single_asset (dir, reel_asset);
551 /* DCP with an over-sized JPEG2000 frame */
552 BOOST_AUTO_TEST_CASE (verify_test15)
554 int const too_big = 1302083 * 2;
556 /* Compress a black image */
557 auto image = black_image ();
558 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
559 BOOST_REQUIRE (frame.size() < too_big);
561 /* Place it in a bigger block with some zero padding at the end */
562 dcp::ArrayData oversized_frame(too_big);
563 memcpy (oversized_frame.data(), frame.data(), frame.size());
564 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
566 boost::filesystem::path const dir("build/test/verify_test15");
567 boost::filesystem::remove_all (dir);
568 dcp_from_frame (oversized_frame, dir);
570 check_verify_result (
573 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES },
574 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
579 /* DCP with a nearly over-sized JPEG2000 frame */
580 BOOST_AUTO_TEST_CASE (verify_test16)
582 int const nearly_too_big = 1302083 * 0.98;
584 /* Compress a black image */
585 auto image = black_image ();
586 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
587 BOOST_REQUIRE (frame.size() < nearly_too_big);
589 /* Place it in a bigger block with some zero padding at the end */
590 dcp::ArrayData oversized_frame(nearly_too_big);
591 memcpy (oversized_frame.data(), frame.data(), frame.size());
592 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
594 boost::filesystem::path const dir("build/test/verify_test16");
595 boost::filesystem::remove_all (dir);
596 dcp_from_frame (oversized_frame, dir);
598 check_verify_result (
601 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES },
602 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
607 /* DCP with a within-range JPEG2000 frame */
608 BOOST_AUTO_TEST_CASE (verify_test17)
610 /* Compress a black image */
611 auto image = black_image ();
612 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
613 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
615 boost::filesystem::path const dir("build/test/verify_test17");
616 boost::filesystem::remove_all (dir);
617 dcp_from_frame (frame, dir);
619 check_verify_result ({ dir }, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
623 /* DCP with valid Interop subtitles */
624 BOOST_AUTO_TEST_CASE (verify_test18)
626 boost::filesystem::path const dir("build/test/verify_test18");
627 prepare_directory (dir);
628 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
629 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
630 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
631 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
633 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
637 /* DCP with broken Interop subtitles */
638 BOOST_AUTO_TEST_CASE (verify_test19)
640 boost::filesystem::path const dir("build/test/verify_test19");
641 prepare_directory (dir);
642 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
643 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
644 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
645 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
648 Editor e (dir / "subs.xml");
649 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
652 check_verify_result (
655 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
656 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
657 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
662 /* DCP with valid SMPTE subtitles */
663 BOOST_AUTO_TEST_CASE (verify_test20)
665 boost::filesystem::path const dir("build/test/verify_test20");
666 prepare_directory (dir);
667 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
668 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
669 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
670 write_dcp_with_single_asset (dir, reel_asset);
672 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
676 /* DCP with broken SMPTE subtitles */
677 BOOST_AUTO_TEST_CASE (verify_test21)
679 boost::filesystem::path const dir("build/test/verify_test21");
680 prepare_directory (dir);
681 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
682 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
683 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
684 write_dcp_with_single_asset (dir, reel_asset);
686 check_verify_result (
689 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
690 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
691 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
692 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
698 BOOST_AUTO_TEST_CASE (verify_test22)
700 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
701 prepare_directory (ov_dir);
703 auto image = black_image ();
704 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
705 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
706 dcp_from_frame (frame, ov_dir);
708 dcp::DCP ov (ov_dir);
711 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
712 prepare_directory (vf_dir);
714 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
716 check_verify_result (
719 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET },
720 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
725 /* DCP with valid CompositionMetadataAsset */
726 BOOST_AUTO_TEST_CASE (verify_test23)
728 boost::filesystem::path const dir("build/test/verify_test23");
729 prepare_directory (dir);
731 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
732 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
733 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
735 auto reel = make_shared<dcp::Reel>();
736 reel->add (reel_asset);
738 reel->add (simple_markers(16 * 24 - 1));
740 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
742 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
743 cpl->set_main_sound_sample_rate (48000);
744 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
745 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
751 dcp::String::compose("libdcp %1", dcp::version),
752 dcp::String::compose("libdcp %1", dcp::version),
753 dcp::LocalTime().as_string(),
757 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
761 boost::filesystem::path find_cpl (boost::filesystem::path dir)
763 for (auto i: boost::filesystem::directory_iterator(dir)) {
764 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
769 BOOST_REQUIRE (false);
774 /* DCP with invalid CompositionMetadataAsset */
775 BOOST_AUTO_TEST_CASE (verify_test24)
777 boost::filesystem::path const dir("build/test/verify_test24");
778 prepare_directory (dir);
780 auto reel = make_shared<dcp::Reel>();
781 reel->add (black_picture_asset(dir));
782 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
784 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
785 cpl->set_main_sound_sample_rate (48000);
786 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
787 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
788 cpl->set_version_number (1);
790 reel->add (simple_markers());
796 dcp::String::compose("libdcp %1", dcp::version),
797 dcp::String::compose("libdcp %1", dcp::version),
798 dcp::LocalTime().as_string(),
803 Editor e (find_cpl("build/test/verify_test24"));
804 e.replace ("MainSound", "MainSoundX");
807 check_verify_result (
810 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
811 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
812 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
813 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
818 /* DCP with invalid CompositionMetadataAsset */
819 BOOST_AUTO_TEST_CASE (verify_test25)
821 boost::filesystem::path const dir("build/test/verify_test25");
822 prepare_directory (dir);
824 auto reel = make_shared<dcp::Reel>();
825 reel->add (black_picture_asset(dir));
826 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
828 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
829 cpl->set_main_sound_sample_rate (48000);
830 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
831 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
837 dcp::String::compose("libdcp %1", dcp::version),
838 dcp::String::compose("libdcp %1", dcp::version),
839 dcp::LocalTime().as_string(),
844 Editor e (find_cpl("build/test/verify_test25"));
845 e.replace ("meta:Width", "meta:WidthX");
848 check_verify_result (
850 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
855 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
856 BOOST_AUTO_TEST_CASE (verify_test26)
858 boost::filesystem::path const dir("build/test/verify_test26");
859 prepare_directory (dir);
860 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
861 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
862 asset->_language = "wrong-andbad";
863 asset->write (dir / "subs.mxf");
864 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
865 reel_asset->_language = "badlang";
866 write_dcp_with_single_asset (dir, reel_asset);
868 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
869 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
870 auto i = notes.begin();
871 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
872 BOOST_REQUIRE (i->note());
873 BOOST_CHECK_EQUAL (*i->note(), "badlang");
875 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
876 BOOST_REQUIRE (i->note());
877 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
879 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
883 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
884 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
886 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
887 prepare_directory (dir);
888 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
889 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
890 asset->_language = "wrong-andbad";
891 asset->write (dir / "subs.mxf");
892 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
893 reel_asset->_language = "badlang";
894 write_dcp_with_single_asset (dir, reel_asset);
896 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
897 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
898 auto i = notes.begin ();
899 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
900 BOOST_REQUIRE (i->note());
901 BOOST_CHECK_EQUAL (*i->note(), "badlang");
903 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
904 BOOST_REQUIRE (i->note());
905 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
907 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
911 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
912 * the release territory.
914 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
916 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
917 prepare_directory (dir);
919 auto picture = simple_picture (dir, "foo");
920 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
921 auto reel = make_shared<dcp::Reel>();
922 reel->add (reel_picture);
923 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
924 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
925 reel->add (reel_sound);
926 reel->add (simple_markers());
928 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
930 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
931 cpl->_additional_subtitle_languages.push_back("andso-is-this");
932 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
933 cpl->set_main_sound_sample_rate (48000);
934 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
935 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
936 cpl->set_version_number (1);
937 cpl->_release_territory = "fred-jim";
938 auto dcp = make_shared<dcp::DCP>(dir);
942 dcp::String::compose("libdcp %1", dcp::version),
943 dcp::String::compose("libdcp %1", dcp::version),
944 dcp::LocalTime().as_string(),
948 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
949 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
950 auto i = notes.begin ();
951 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
952 BOOST_REQUIRE (i->note());
953 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
955 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
956 BOOST_REQUIRE (i->note());
957 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
959 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
960 BOOST_REQUIRE (i->note());
961 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
963 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
964 BOOST_REQUIRE (i->note());
965 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
970 vector<dcp::VerificationNote>
971 check_picture_size (int width, int height, int frame_rate, bool three_d)
973 using namespace boost::filesystem;
975 path dcp_path = "build/test/verify_picture_test";
976 remove_all (dcp_path);
977 create_directories (dcp_path);
979 shared_ptr<dcp::PictureAsset> mp;
981 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
983 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
985 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
987 auto image = black_image (dcp::Size(width, height));
988 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
989 int const length = three_d ? frame_rate * 2 : frame_rate;
990 for (int i = 0; i < length; ++i) {
991 picture_writer->write (j2c.data(), j2c.size());
993 picture_writer->finalize ();
995 auto d = make_shared<dcp::DCP>(dcp_path);
996 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
997 cpl->set_annotation_text ("A Test DCP");
998 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
999 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
1000 cpl->set_main_sound_sample_rate (48000);
1001 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
1002 cpl->set_main_picture_active_area (dcp::Size(1998, 1080));
1003 cpl->set_version_number (1);
1005 auto reel = make_shared<dcp::Reel>();
1008 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
1010 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
1013 reel->add (simple_markers(frame_rate));
1020 dcp::String::compose("libdcp %1", dcp::version),
1021 dcp::String::compose("libdcp %1", dcp::version),
1022 dcp::LocalTime().as_string(),
1026 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1032 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1034 auto notes = check_picture_size(width, height, frame_rate, three_d);
1036 BOOST_CHECK_EQUAL (notes.size(), 0U);
1042 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1044 auto notes = check_picture_size(width, height, frame_rate, three_d);
1045 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1046 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1047 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
1053 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1055 auto notes = check_picture_size(width, height, frame_rate, three_d);
1056 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1057 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1058 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
1064 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1066 auto notes = check_picture_size(width, height, frame_rate, three_d);
1067 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1068 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1069 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1073 BOOST_AUTO_TEST_CASE (verify_picture_size)
1075 using namespace boost::filesystem;
1078 check_picture_size_ok (2048, 858, 24, false);
1079 check_picture_size_ok (2048, 858, 25, false);
1080 check_picture_size_ok (2048, 858, 48, false);
1081 check_picture_size_ok (2048, 858, 24, true);
1082 check_picture_size_ok (2048, 858, 25, true);
1083 check_picture_size_ok (2048, 858, 48, true);
1086 check_picture_size_ok (1998, 1080, 24, false);
1087 check_picture_size_ok (1998, 1080, 25, false);
1088 check_picture_size_ok (1998, 1080, 48, false);
1089 check_picture_size_ok (1998, 1080, 24, true);
1090 check_picture_size_ok (1998, 1080, 25, true);
1091 check_picture_size_ok (1998, 1080, 48, true);
1094 check_picture_size_ok (4096, 1716, 24, false);
1097 check_picture_size_ok (3996, 2160, 24, false);
1099 /* Bad frame size */
1100 check_picture_size_bad_frame_size (2050, 858, 24, false);
1101 check_picture_size_bad_frame_size (2048, 658, 25, false);
1102 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1103 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1105 /* Bad 2K frame rate */
1106 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1107 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1108 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1110 /* Bad 4K frame rate */
1111 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1112 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1115 auto notes = check_picture_size(3996, 2160, 24, true);
1116 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1117 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1118 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1124 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1127 make_shared<dcp::SubtitleString>(
1135 dcp::Time(start_frame, 24, 24),
1136 dcp::Time(end_frame, 24, 24),
1152 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1154 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1155 prepare_directory (dir);
1157 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1158 for (int i = 0; i < 2048; ++i) {
1159 add_test_subtitle (asset, i * 24, i * 24 + 20);
1161 asset->set_language (dcp::LanguageTag("de-DE"));
1162 asset->write (dir / "subs.mxf");
1163 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1164 write_dcp_with_single_asset (dir, reel_asset);
1166 check_verify_result (
1169 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1170 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1171 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1172 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1178 shared_ptr<dcp::SMPTESubtitleAsset>
1179 make_large_subtitle_asset (boost::filesystem::path font_file)
1181 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1182 dcp::ArrayData big_fake_font(1024 * 1024);
1183 big_fake_font.write (font_file);
1184 for (int i = 0; i < 116; ++i) {
1185 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1193 verify_timed_text_asset_too_large (string name)
1195 auto const dir = boost::filesystem::path("build/test") / name;
1196 prepare_directory (dir);
1197 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1198 add_test_subtitle (asset, 0, 20);
1199 asset->set_language (dcp::LanguageTag("de-DE"));
1200 asset->write (dir / "subs.mxf");
1202 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1203 write_dcp_with_single_asset (dir, reel_asset);
1205 check_verify_result (
1208 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1209 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1210 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1211 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1212 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1217 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1219 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1220 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1224 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1226 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1227 prepare_directory (dir);
1228 auto dcp = make_simple (dir, 1, 240);
1231 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1232 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1233 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1234 "<ContentTitleText>Content</ContentTitleText>"
1235 "<AnnotationText>Annotation</AnnotationText>"
1236 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1237 "<ReelNumber>1</ReelNumber>"
1238 "<EditRate>25 1</EditRate>"
1239 "<TimeCodeRate>25</TimeCodeRate>"
1240 "<StartTime>00:00:00:00</StartTime>"
1241 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1243 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1244 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1245 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1251 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1252 BOOST_REQUIRE (xml_file);
1253 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1255 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1256 subs->write (dir / "subs.mxf");
1258 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1259 dcp->cpls().front()->reels().front()->add(reel_subs);
1262 dcp::String::compose("libdcp %1", dcp::version),
1263 dcp::String::compose("libdcp %1", dcp::version),
1264 dcp::LocalTime().as_string(),
1268 check_verify_result (
1271 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1272 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1277 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1279 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1280 auto dcp = make_simple (path, 2, 240);
1281 auto cpl = dcp->cpls()[0];
1284 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1285 subs->set_language (dcp::LanguageTag("de-DE"));
1286 subs->add (simple_subtitle());
1287 subs->write (path / "subs1.mxf");
1288 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1289 cpl->reels()[0]->add(reel_subs);
1293 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1294 subs->set_language (dcp::LanguageTag("en-US"));
1295 subs->add (simple_subtitle());
1296 subs->write (path / "subs2.mxf");
1297 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1298 cpl->reels()[1]->add(reel_subs);
1303 dcp::String::compose("libdcp %1", dcp::version),
1304 dcp::String::compose("libdcp %1", dcp::version),
1305 dcp::LocalTime().as_string(),
1309 check_verify_result (
1312 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1313 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1314 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1319 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1321 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1322 prepare_directory (dir);
1323 auto dcp = make_simple (dir, 1, 240);
1326 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1327 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1328 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1329 "<ContentTitleText>Content</ContentTitleText>"
1330 "<AnnotationText>Annotation</AnnotationText>"
1331 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1332 "<ReelNumber>1</ReelNumber>"
1333 "<Language>de-DE</Language>"
1334 "<EditRate>25 1</EditRate>"
1335 "<TimeCodeRate>25</TimeCodeRate>"
1336 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1338 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1339 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1340 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1346 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1347 BOOST_REQUIRE (xml_file);
1348 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1350 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1351 subs->write (dir / "subs.mxf");
1353 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1354 dcp->cpls().front()->reels().front()->add(reel_subs);
1357 dcp::String::compose("libdcp %1", dcp::version),
1358 dcp::String::compose("libdcp %1", dcp::version),
1359 dcp::LocalTime().as_string(),
1363 check_verify_result (
1366 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1367 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1372 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1374 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1375 prepare_directory (dir);
1376 auto dcp = make_simple (dir, 1, 240);
1379 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1380 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1381 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1382 "<ContentTitleText>Content</ContentTitleText>"
1383 "<AnnotationText>Annotation</AnnotationText>"
1384 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1385 "<ReelNumber>1</ReelNumber>"
1386 "<Language>de-DE</Language>"
1387 "<EditRate>25 1</EditRate>"
1388 "<TimeCodeRate>25</TimeCodeRate>"
1389 "<StartTime>00:00:02:00</StartTime>"
1390 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1392 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1393 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1394 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1400 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1401 BOOST_REQUIRE (xml_file);
1402 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1404 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1405 subs->write (dir / "subs.mxf");
1407 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1408 dcp->cpls().front()->reels().front()->add(reel_subs);
1411 dcp::String::compose("libdcp %1", dcp::version),
1412 dcp::String::compose("libdcp %1", dcp::version),
1413 dcp::LocalTime().as_string(),
1417 check_verify_result (
1420 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1421 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1429 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1432 , v_position(v_position_)
1445 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1447 prepare_directory (dir);
1448 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1449 asset->set_start_time (dcp::Time());
1450 for (auto i: subs) {
1451 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1453 asset->set_language (dcp::LanguageTag("de-DE"));
1454 asset->write (dir / "subs.mxf");
1456 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1457 write_dcp_with_single_asset (dir, reel_asset);
1461 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1463 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1464 /* Just too early */
1465 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1466 check_verify_result (
1469 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1470 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1476 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1478 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1479 /* Just late enough */
1480 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1481 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1485 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1487 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1488 prepare_directory (dir);
1490 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1491 asset1->set_start_time (dcp::Time());
1492 /* Just late enough */
1493 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1494 asset1->set_language (dcp::LanguageTag("de-DE"));
1495 asset1->write (dir / "subs1.mxf");
1496 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1497 auto reel1 = make_shared<dcp::Reel>();
1498 reel1->add (reel_asset1);
1499 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1500 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1501 reel1->add (markers1);
1503 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1504 asset2->set_start_time (dcp::Time());
1505 /* This would be too early on first reel but should be OK on the second */
1506 add_test_subtitle (asset2, 0, 4 * 24);
1507 asset2->set_language (dcp::LanguageTag("de-DE"));
1508 asset2->write (dir / "subs2.mxf");
1509 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1510 auto reel2 = make_shared<dcp::Reel>();
1511 reel2->add (reel_asset2);
1512 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1513 markers2->set (dcp::Marker::LFOC, dcp::Time(16 * 24 - 1, 24, 24));
1514 reel2->add (markers2);
1516 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1519 auto dcp = make_shared<dcp::DCP>(dir);
1523 dcp::String::compose("libdcp %1", dcp::version),
1524 dcp::String::compose("libdcp %1", dcp::version),
1525 dcp::LocalTime().as_string(),
1530 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1534 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1536 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1537 dcp_with_text<dcp::ReelSubtitleAsset> (
1541 { 5 * 24 + 1, 6 * 24 },
1543 check_verify_result (
1546 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE },
1547 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1552 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1554 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1555 dcp_with_text<dcp::ReelSubtitleAsset> (
1559 { 5 * 24 + 16, 8 * 24 },
1561 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1565 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1567 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1568 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1569 check_verify_result (
1572 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT },
1573 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1578 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1580 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1581 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1582 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1586 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1588 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1589 dcp_with_text<dcp::ReelSubtitleAsset> (
1592 { 96, 200, 0.0, "We" },
1593 { 96, 200, 0.1, "have" },
1594 { 96, 200, 0.2, "four" },
1595 { 96, 200, 0.3, "lines" }
1597 check_verify_result (
1600 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1601 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1606 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1608 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1609 dcp_with_text<dcp::ReelSubtitleAsset> (
1612 { 96, 200, 0.0, "We" },
1613 { 96, 200, 0.1, "have" },
1614 { 96, 200, 0.2, "four" },
1616 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1620 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1622 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1623 dcp_with_text<dcp::ReelSubtitleAsset> (
1626 { 96, 300, 0.0, "We" },
1627 { 96, 300, 0.1, "have" },
1628 { 150, 180, 0.2, "four" },
1629 { 150, 180, 0.3, "lines" }
1631 check_verify_result (
1634 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1635 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1640 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1642 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1643 dcp_with_text<dcp::ReelSubtitleAsset> (
1646 { 96, 300, 0.0, "We" },
1647 { 96, 300, 0.1, "have" },
1648 { 150, 180, 0.2, "four" },
1649 { 190, 250, 0.3, "lines" }
1651 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1655 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1657 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1658 dcp_with_text<dcp::ReelSubtitleAsset> (
1661 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1663 check_verify_result (
1666 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED },
1667 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1672 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1674 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1675 dcp_with_text<dcp::ReelSubtitleAsset> (
1678 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1680 check_verify_result (
1683 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG },
1684 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1689 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1691 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1692 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1695 { 96, 200, 0.0, "We" },
1696 { 96, 200, 0.1, "have" },
1697 { 96, 200, 0.2, "four" },
1698 { 96, 200, 0.3, "lines" }
1700 check_verify_result (
1703 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1704 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1709 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1711 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1712 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1715 { 96, 200, 0.0, "We" },
1716 { 96, 200, 0.1, "have" },
1717 { 96, 200, 0.2, "four" },
1719 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1723 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1725 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1726 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1729 { 96, 300, 0.0, "We" },
1730 { 96, 300, 0.1, "have" },
1731 { 150, 180, 0.2, "four" },
1732 { 150, 180, 0.3, "lines" }
1734 check_verify_result (
1737 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1738 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1743 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1745 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1746 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1749 { 96, 300, 0.0, "We" },
1750 { 96, 300, 0.1, "have" },
1751 { 150, 180, 0.2, "four" },
1752 { 190, 250, 0.3, "lines" }
1754 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1758 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1760 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1761 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1764 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1766 check_verify_result (
1769 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG },
1770 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1775 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1777 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1778 prepare_directory (dir);
1780 auto picture = simple_picture (dir, "foo");
1781 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1782 auto reel = make_shared<dcp::Reel>();
1783 reel->add (reel_picture);
1784 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1785 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1786 reel->add (reel_sound);
1787 reel->add (simple_markers());
1788 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1790 auto dcp = make_shared<dcp::DCP>(dir);
1794 dcp::String::compose("libdcp %1", dcp::version),
1795 dcp::String::compose("libdcp %1", dcp::version),
1796 dcp::LocalTime().as_string(),
1800 check_verify_result (
1803 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE },
1804 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1809 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1811 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1812 auto dcp = make_simple (dir);
1815 dcp::String::compose("libdcp %1", dcp::version),
1816 dcp::String::compose("libdcp %1", dcp::version),
1817 dcp::LocalTime().as_string(),
1821 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1824 BOOST_REQUIRE (dcp->cpls()[0]->file());
1825 Editor e(dcp->cpls()[0]->file().get());
1826 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1829 check_verify_result (
1832 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1833 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1838 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1840 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1841 auto dcp = make_simple (dir);
1844 dcp::String::compose("libdcp %1", dcp::version),
1845 dcp::String::compose("libdcp %1", dcp::version),
1846 dcp::LocalTime().as_string(),
1850 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1853 BOOST_REQUIRE (dcp->cpls()[0]->file());
1854 Editor e(dcp->cpls()[0]->file().get());
1855 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1858 check_verify_result (
1861 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1862 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1867 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1869 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1870 boost::filesystem::remove_all (dir);
1871 boost::filesystem::create_directories (dir);
1872 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1873 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1875 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1876 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1878 auto reel = make_shared<dcp::Reel>(
1879 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1880 make_shared<dcp::ReelSoundAsset>(ms, 0)
1883 reel->add (simple_markers());
1889 dcp::String::compose("libdcp %1", dcp::version),
1890 dcp::String::compose("libdcp %1", dcp::version),
1891 dcp::LocalTime().as_string(),
1896 check_verify_result (
1899 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION },
1900 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1908 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1910 boost::filesystem::remove_all (dir);
1911 boost::filesystem::create_directories (dir);
1912 auto dcp = make_shared<dcp::DCP>(dir);
1913 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1915 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1916 subs->set_language (dcp::LanguageTag("de-DE"));
1917 subs->set_start_time (dcp::Time());
1918 subs->add (simple_subtitle());
1919 subs->write (dir / "subs.mxf");
1920 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1922 auto reel1 = make_shared<dcp::Reel>(
1923 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1924 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1928 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1931 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1932 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1933 reel1->add (markers1);
1937 auto reel2 = make_shared<dcp::Reel>(
1938 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1939 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1943 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1946 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1947 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1948 reel2->add (markers2);
1955 dcp::String::compose("libdcp %1", dcp::version),
1956 dcp::String::compose("libdcp %1", dcp::version),
1957 dcp::LocalTime().as_string(),
1963 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1966 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1967 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1968 check_verify_result (
1971 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS },
1972 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1978 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1979 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1980 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1984 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1985 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1986 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1993 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1995 boost::filesystem::remove_all (dir);
1996 boost::filesystem::create_directories (dir);
1997 auto dcp = make_shared<dcp::DCP>(dir);
1998 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2000 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2001 subs->set_language (dcp::LanguageTag("de-DE"));
2002 subs->set_start_time (dcp::Time());
2003 subs->add (simple_subtitle());
2004 subs->write (dir / "subs.mxf");
2006 auto reel1 = make_shared<dcp::Reel>(
2007 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2008 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2011 for (int i = 0; i < caps_in_reel1; ++i) {
2012 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2015 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
2016 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
2017 reel1->add (markers1);
2021 auto reel2 = make_shared<dcp::Reel>(
2022 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2023 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2026 for (int i = 0; i < caps_in_reel2; ++i) {
2027 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2030 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
2031 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
2032 reel2->add (markers2);
2039 dcp::String::compose("libdcp %1", dcp::version),
2040 dcp::String::compose("libdcp %1", dcp::version),
2041 dcp::LocalTime().as_string(),
2047 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
2050 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
2051 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2052 check_verify_result (
2055 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER },
2056 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2061 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2062 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2063 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2067 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2068 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2069 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2076 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2078 boost::filesystem::remove_all (dir);
2079 boost::filesystem::create_directories (dir);
2080 auto dcp = make_shared<dcp::DCP>(dir);
2081 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2083 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2084 subs->set_language (dcp::LanguageTag("de-DE"));
2085 subs->set_start_time (dcp::Time());
2086 subs->add (simple_subtitle());
2087 subs->write (dir / "subs.mxf");
2088 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
2091 auto reel = make_shared<dcp::Reel>(
2092 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2093 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2096 reel->add (reel_text);
2098 reel->add (simple_markers(240));
2105 dcp::String::compose("libdcp %1", dcp::version),
2106 dcp::String::compose("libdcp %1", dcp::version),
2107 dcp::LocalTime().as_string(),
2111 check_verify_result (
2114 { dcp::VerificationNote::VERIFY_BV21_ERROR, code },
2115 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2120 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2122 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2123 "build/test/verify_subtitle_entry_point_must_be_present",
2124 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
2125 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2126 asset->unset_entry_point ();
2130 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2131 "build/test/verify_subtitle_entry_point_must_be_zero",
2132 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
2133 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2134 asset->set_entry_point (4);
2138 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2139 "build/test/verify_closed_caption_entry_point_must_be_present",
2140 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2141 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2142 asset->unset_entry_point ();
2146 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2147 "build/test/verify_closed_caption_entry_point_must_be_zero",
2148 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
2149 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2150 asset->set_entry_point (9);
2156 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
2160 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
2161 auto dcp = make_simple (dir);
2164 dcp::String::compose("libdcp %1", dcp::version),
2165 dcp::String::compose("libdcp %1", dcp::version),
2166 dcp::LocalTime().as_string(),
2170 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2173 BOOST_REQUIRE (dcp->cpls()[0]->file());
2174 Editor e(dcp->cpls()[0]->file().get());
2175 e.replace("<Hash>XGhFVrqZqapOJx5Fh2SLjj48Yjg=</Hash>", "");
2178 check_verify_result (
2181 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2182 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
2189 verify_markers_test (
2190 boost::filesystem::path dir,
2191 vector<pair<dcp::Marker, dcp::Time>> markers,
2192 vector<dcp::VerificationNote> test_notes
2195 auto dcp = make_simple (dir);
2196 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
2197 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24, 0);
2198 for (auto const& i: markers) {
2199 markers_asset->set (i.first, i.second);
2201 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2204 dcp::String::compose("libdcp %1", dcp::version),
2205 dcp::String::compose("libdcp %1", dcp::version),
2206 dcp::LocalTime().as_string(),
2210 check_verify_result ({dir}, test_notes);
2214 BOOST_AUTO_TEST_CASE (verify_markers)
2216 verify_markers_test (
2217 "build/test/verify_markers_all_correct",
2219 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2220 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2221 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2222 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2227 verify_markers_test (
2228 "build/test/verify_markers_missing_ffec",
2230 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2231 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2232 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2235 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2238 verify_markers_test (
2239 "build/test/verify_markers_missing_ffmc",
2241 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2242 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2243 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2246 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2249 verify_markers_test (
2250 "build/test/verify_markers_missing_ffoc",
2252 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2253 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2254 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2257 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2260 verify_markers_test (
2261 "build/test/verify_markers_missing_lfoc",
2263 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2264 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2265 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2268 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2271 verify_markers_test (
2272 "build/test/verify_markers_incorrect_ffoc",
2274 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2275 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2276 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2277 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2280 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2283 verify_markers_test (
2284 "build/test/verify_markers_incorrect_lfoc",
2286 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2287 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2288 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2289 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2292 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }
2297 BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
2299 boost::filesystem::path dir = "build/test/verify_cpl_metadata_version";
2300 prepare_directory (dir);
2301 auto dcp = make_simple (dir);
2302 dcp->cpls()[0]->unset_version_number();
2305 dcp::String::compose("libdcp %1", dcp::version),
2306 dcp::String::compose("libdcp %1", dcp::version),
2307 dcp::LocalTime().as_string(),
2311 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});
2315 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata1)
2317 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata1";
2318 auto dcp = make_simple (dir);
2321 dcp::String::compose("libdcp %1", dcp::version),
2322 dcp::String::compose("libdcp %1", dcp::version),
2323 dcp::LocalTime().as_string(),
2328 Editor e (dcp->cpls()[0]->file().get());
2329 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2332 check_verify_result (
2335 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2336 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2341 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata2)
2343 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata2";
2344 auto dcp = make_simple (dir);
2347 dcp::String::compose("libdcp %1", dcp::version),
2348 dcp::String::compose("libdcp %1", dcp::version),
2349 dcp::LocalTime().as_string(),
2354 Editor e (dcp->cpls()[0]->file().get());
2355 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2358 check_verify_result (
2361 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2362 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2367 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata3)
2369 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata3";
2370 auto dcp = make_simple (dir);
2373 dcp::String::compose("libdcp %1", dcp::version),
2374 dcp::String::compose("libdcp %1", dcp::version),
2375 dcp::LocalTime().as_string(),
2380 Editor e (dcp->cpls()[0]->file().get());
2381 e.replace ("<meta:Name>A", "<meta:NameX>A");
2382 e.replace ("n</meta:Name>", "n</meta:NameX>");
2385 check_verify_result (
2388 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2389 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2390 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2395 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata4)
2397 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata4";
2398 auto dcp = make_simple (dir);
2401 dcp::String::compose("libdcp %1", dcp::version),
2402 dcp::String::compose("libdcp %1", dcp::version),
2403 dcp::LocalTime().as_string(),
2408 Editor e (dcp->cpls()[0]->file().get());
2409 e.replace ("Application", "Fred");
2412 check_verify_result (
2415 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2416 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'Application'") },
2421 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata5)
2423 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata5";
2424 auto dcp = make_simple (dir);
2427 dcp::String::compose("libdcp %1", dcp::version),
2428 dcp::String::compose("libdcp %1", dcp::version),
2429 dcp::LocalTime().as_string(),
2433 Editor e (dcp->cpls()[0]->file().get());
2434 e.replace ("DCP Constraints Profile", "Fred");
2437 check_verify_result (
2440 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2441 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'") },
2446 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata6)
2448 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata6";
2449 auto dcp = make_simple (dir);
2452 dcp::String::compose("libdcp %1", dcp::version),
2453 dcp::String::compose("libdcp %1", dcp::version),
2454 dcp::LocalTime().as_string(),
2459 Editor e (dcp->cpls()[0]->file().get());
2460 e.replace ("<meta:Value>", "<meta:ValueX>");
2461 e.replace ("</meta:Value>", "</meta:ValueX>");
2464 check_verify_result (
2467 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2468 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2469 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2474 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata7)
2476 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata7";
2477 auto dcp = make_simple (dir);
2480 dcp::String::compose("libdcp %1", dcp::version),
2481 dcp::String::compose("libdcp %1", dcp::version),
2482 dcp::LocalTime().as_string(),
2486 Editor e (dcp->cpls()[0]->file().get());
2487 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2490 check_verify_result (
2493 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2494 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'") },
2499 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata8)
2501 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata8";
2502 auto dcp = make_simple (dir);
2505 dcp::String::compose("libdcp %1", dcp::version),
2506 dcp::String::compose("libdcp %1", dcp::version),
2507 dcp::LocalTime().as_string(),
2511 Editor e (dcp->cpls()[0]->file().get());
2512 e.replace ("<meta:Property>", "<meta:PropertyX>");
2513 e.replace ("</meta:Property>", "</meta:PropertyX>");
2516 check_verify_result (
2519 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2520 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2521 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2526 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata9)
2528 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata9";
2529 auto dcp = make_simple (dir);
2532 dcp::String::compose("libdcp %1", dcp::version),
2533 dcp::String::compose("libdcp %1", dcp::version),
2534 dcp::LocalTime().as_string(),
2538 Editor e (dcp->cpls()[0]->file().get());
2539 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2540 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2543 check_verify_result (
2546 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2547 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2548 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2554 BOOST_AUTO_TEST_CASE (verify_encrypted_cpl_is_signed)
2556 boost::filesystem::path dir = "build/test/verify_encrypted_cpl_is_signed";
2557 prepare_directory (dir);
2558 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2559 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2563 Editor e (dir / "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
2564 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2567 check_verify_result (
2570 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2571 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_ANNOTATION_TEXT_DOES_NOT_MATCH_CPL_CONTENT_TITLE_TEXT },
2572 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
2573 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE },
2574 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC },
2575 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC },
2576 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_CPL_METADATA },
2577 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CPL_WITH_ENCRYPTED_CONTENT_NOT_SIGNED }
2582 BOOST_AUTO_TEST_CASE (verify_encrypted_pkl_is_signed)
2584 boost::filesystem::path dir = "build/test/verify_encrypted_pkl_is_signed";
2585 prepare_directory (dir);
2586 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2587 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2591 Editor e (dir / "pkl_93182bd2-b1e8-41a3-b5c8-6e6564273bff.xml");
2592 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2595 check_verify_result (
2598 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_ANNOTATION_TEXT_DOES_NOT_MATCH_CPL_CONTENT_TITLE_TEXT },
2599 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
2600 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE },
2601 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC },
2602 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC },
2603 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_CPL_METADATA },
2604 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_WITH_ENCRYPTED_CONTENT_NOT_SIGNED }
2609 BOOST_AUTO_TEST_CASE (verify_unencrypted_pkl_can_be_unsigned)
2611 boost::filesystem::path dir = "build/test/verify_unencrypted_pkl_can_be_unsigned";
2612 prepare_directory (dir);
2613 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/dcp_test1")) {
2614 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2618 Editor e (dir / "pkl_2b9b857f-ab4a-440e-a313-1ace0f1cfc95.xml");
2619 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2622 check_verify_result ({dir}, {});
2626 BOOST_AUTO_TEST_CASE (verify_must_not_be_partially_encrypted)
2628 boost::filesystem::path dir ("build/test/verify_must_not_be_partially_encrypted");
2629 prepare_directory (dir);
2633 auto signer = make_shared<dcp::CertificateChain>();
2634 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
2635 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
2636 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
2637 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
2639 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2643 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::SMPTE);
2646 auto writer = mp->start_write (dir / "video.mxf", false);
2647 dcp::ArrayData j2c ("test/data/flat_red.j2c");
2648 for (int i = 0; i < 24; ++i) {
2649 writer->write (j2c.data(), j2c.size());
2651 writer->finalize ();
2653 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
2655 auto reel = make_shared<dcp::Reel>(
2656 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
2657 make_shared<dcp::ReelSoundAsset>(ms, 0)
2660 reel->add (simple_markers());
2664 cpl->set_content_version (
2665 {"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"}
2667 cpl->set_annotation_text ("A Test DCP");
2668 cpl->set_issuer ("OpenDCP 0.0.25");
2669 cpl->set_creator ("OpenDCP 0.0.25");
2670 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
2671 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
2672 cpl->set_main_sound_sample_rate (48000);
2673 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
2674 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
2675 cpl->set_version_number (1);
2679 d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "A Test DCP", signer);
2681 check_verify_result ({dir}, {{dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PARTIALLY_ENCRYPTED}});