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 check_verify_result (
341 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ, string("Bad content kind 'xtrailer'")}}
346 boost::filesystem::path
349 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_cpl);
353 boost::filesystem::path
356 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_pkl);
360 boost::filesystem::path
363 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
368 BOOST_AUTO_TEST_CASE (verify_test5)
370 check_verify_result_after_replace (
372 "<FrameRate>24 1", "<FrameRate>99 1",
373 { dcp::VerificationNote::CPL_HASH_INCORRECT,
374 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
379 BOOST_AUTO_TEST_CASE (verify_test6)
381 auto directories = setup (1, 6);
383 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
384 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
388 boost::filesystem::path
391 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
394 /* Empty asset filename in ASSETMAP */
395 BOOST_AUTO_TEST_CASE (verify_test7)
397 check_verify_result_after_replace (
399 "<Path>video.mxf</Path>", "<Path></Path>",
400 { dcp::VerificationNote::EMPTY_ASSET_PATH }
404 /* Mismatched standard */
405 BOOST_AUTO_TEST_CASE (verify_test8)
407 check_verify_result_after_replace (
409 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
410 { dcp::VerificationNote::MISMATCHED_STANDARD,
411 dcp::VerificationNote::XML_VALIDATION_ERROR,
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::CPL_HASH_INCORRECT }
420 /* Badly formatted <Id> in CPL */
421 BOOST_AUTO_TEST_CASE (verify_test9)
423 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
424 check_verify_result_after_replace (
426 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
427 { dcp::VerificationNote::XML_VALIDATION_ERROR }
431 /* Badly formatted <IssueDate> in CPL */
432 BOOST_AUTO_TEST_CASE (verify_test10)
434 check_verify_result_after_replace (
436 "<IssueDate>", "<IssueDate>x",
437 { dcp::VerificationNote::XML_VALIDATION_ERROR,
438 dcp::VerificationNote::CPL_HASH_INCORRECT }
442 /* Badly-formatted <Id> in PKL */
443 BOOST_AUTO_TEST_CASE (verify_test11)
445 check_verify_result_after_replace (
447 "<Id>urn:uuid:2b9", "<Id>urn:uuid:xb9",
448 { dcp::VerificationNote::XML_VALIDATION_ERROR }
452 /* Badly-formatted <Id> in ASSETMAP */
453 BOOST_AUTO_TEST_CASE (verify_test12)
455 check_verify_result_after_replace (
457 "<Id>urn:uuid:07e", "<Id>urn:uuid:x7e",
458 { dcp::VerificationNote::XML_VALIDATION_ERROR }
462 /* Basic test of an Interop DCP */
463 BOOST_AUTO_TEST_CASE (verify_test13)
466 auto directories = setup (3, 13);
467 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
469 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
470 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
471 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
473 auto st = stages.begin();
474 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
475 BOOST_REQUIRE (st->second);
476 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
478 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
479 BOOST_REQUIRE (st->second);
480 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
482 BOOST_CHECK_EQUAL (st->first, "Checking reel");
483 BOOST_REQUIRE (!st->second);
485 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
486 BOOST_REQUIRE (st->second);
487 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
489 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
490 BOOST_REQUIRE (st->second);
491 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
493 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
494 BOOST_REQUIRE (st->second);
495 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
497 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
498 BOOST_REQUIRE (st->second);
499 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
501 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
502 BOOST_REQUIRE (st->second);
503 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
505 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
506 BOOST_REQUIRE (st->second);
507 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
509 BOOST_REQUIRE (st == stages.end());
511 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
512 auto i = notes.begin ();
513 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
514 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
517 /* DCP with a short asset */
518 BOOST_AUTO_TEST_CASE (verify_test14)
520 auto directories = setup (8, 14);
521 check_verify_result (
524 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
525 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
526 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
527 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
528 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
535 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
537 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
538 boost::filesystem::create_directories (dir);
539 auto writer = asset->start_write (dir / "pic.mxf", true);
540 for (int i = 0; i < 24; ++i) {
541 writer->write (frame.data(), frame.size());
545 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
546 write_dcp_with_single_asset (dir, reel_asset);
550 /* DCP with an over-sized JPEG2000 frame */
551 BOOST_AUTO_TEST_CASE (verify_test15)
553 int const too_big = 1302083 * 2;
555 /* Compress a black image */
556 auto image = black_image ();
557 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
558 BOOST_REQUIRE (frame.size() < too_big);
560 /* Place it in a bigger block with some zero padding at the end */
561 dcp::ArrayData oversized_frame(too_big);
562 memcpy (oversized_frame.data(), frame.data(), frame.size());
563 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
565 boost::filesystem::path const dir("build/test/verify_test15");
566 boost::filesystem::remove_all (dir);
567 dcp_from_frame (oversized_frame, dir);
569 check_verify_result (
572 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES },
573 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
578 /* DCP with a nearly over-sized JPEG2000 frame */
579 BOOST_AUTO_TEST_CASE (verify_test16)
581 int const nearly_too_big = 1302083 * 0.98;
583 /* Compress a black image */
584 auto image = black_image ();
585 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
586 BOOST_REQUIRE (frame.size() < nearly_too_big);
588 /* Place it in a bigger block with some zero padding at the end */
589 dcp::ArrayData oversized_frame(nearly_too_big);
590 memcpy (oversized_frame.data(), frame.data(), frame.size());
591 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
593 boost::filesystem::path const dir("build/test/verify_test16");
594 boost::filesystem::remove_all (dir);
595 dcp_from_frame (oversized_frame, dir);
597 check_verify_result (
600 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES },
601 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
606 /* DCP with a within-range JPEG2000 frame */
607 BOOST_AUTO_TEST_CASE (verify_test17)
609 /* Compress a black image */
610 auto image = black_image ();
611 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
612 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
614 boost::filesystem::path const dir("build/test/verify_test17");
615 boost::filesystem::remove_all (dir);
616 dcp_from_frame (frame, dir);
618 check_verify_result ({ dir }, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
622 /* DCP with valid Interop subtitles */
623 BOOST_AUTO_TEST_CASE (verify_test18)
625 boost::filesystem::path const dir("build/test/verify_test18");
626 prepare_directory (dir);
627 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
628 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
629 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
630 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
632 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
636 /* DCP with broken Interop subtitles */
637 BOOST_AUTO_TEST_CASE (verify_test19)
639 boost::filesystem::path const dir("build/test/verify_test19");
640 prepare_directory (dir);
641 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
642 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
643 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
644 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
647 Editor e (dir / "subs.xml");
648 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
651 check_verify_result (
654 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
655 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
656 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
661 /* DCP with valid SMPTE subtitles */
662 BOOST_AUTO_TEST_CASE (verify_test20)
664 boost::filesystem::path const dir("build/test/verify_test20");
665 prepare_directory (dir);
666 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
667 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
668 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
669 write_dcp_with_single_asset (dir, reel_asset);
671 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
675 /* DCP with broken SMPTE subtitles */
676 BOOST_AUTO_TEST_CASE (verify_test21)
678 boost::filesystem::path const dir("build/test/verify_test21");
679 prepare_directory (dir);
680 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
681 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
682 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
683 write_dcp_with_single_asset (dir, reel_asset);
685 check_verify_result (
688 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
689 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
690 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
691 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
697 BOOST_AUTO_TEST_CASE (verify_test22)
699 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
700 prepare_directory (ov_dir);
702 auto image = black_image ();
703 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
704 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
705 dcp_from_frame (frame, ov_dir);
707 dcp::DCP ov (ov_dir);
710 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
711 prepare_directory (vf_dir);
713 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
715 check_verify_result (
718 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET },
719 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
724 /* DCP with valid CompositionMetadataAsset */
725 BOOST_AUTO_TEST_CASE (verify_test23)
727 boost::filesystem::path const dir("build/test/verify_test23");
728 prepare_directory (dir);
730 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
731 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
732 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
734 auto reel = make_shared<dcp::Reel>();
735 reel->add (reel_asset);
737 reel->add (simple_markers(16 * 24 - 1));
739 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
741 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
742 cpl->set_main_sound_sample_rate (48000);
743 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
744 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
750 dcp::String::compose("libdcp %1", dcp::version),
751 dcp::String::compose("libdcp %1", dcp::version),
752 dcp::LocalTime().as_string(),
756 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
760 boost::filesystem::path find_cpl (boost::filesystem::path dir)
762 for (auto i: boost::filesystem::directory_iterator(dir)) {
763 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
768 BOOST_REQUIRE (false);
773 /* DCP with invalid CompositionMetadataAsset */
774 BOOST_AUTO_TEST_CASE (verify_test24)
776 boost::filesystem::path const dir("build/test/verify_test24");
777 prepare_directory (dir);
779 auto reel = make_shared<dcp::Reel>();
780 reel->add (black_picture_asset(dir));
781 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
783 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
784 cpl->set_main_sound_sample_rate (48000);
785 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
786 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
787 cpl->set_version_number (1);
789 reel->add (simple_markers());
795 dcp::String::compose("libdcp %1", dcp::version),
796 dcp::String::compose("libdcp %1", dcp::version),
797 dcp::LocalTime().as_string(),
802 Editor e (find_cpl("build/test/verify_test24"));
803 e.replace ("MainSound", "MainSoundX");
806 check_verify_result (
809 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
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::CPL_HASH_INCORRECT }
817 /* DCP with invalid CompositionMetadataAsset */
818 BOOST_AUTO_TEST_CASE (verify_test25)
820 boost::filesystem::path const dir("build/test/verify_test25");
821 prepare_directory (dir);
823 auto reel = make_shared<dcp::Reel>();
824 reel->add (black_picture_asset(dir));
825 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
827 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
828 cpl->set_main_sound_sample_rate (48000);
829 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
830 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
836 dcp::String::compose("libdcp %1", dcp::version),
837 dcp::String::compose("libdcp %1", dcp::version),
838 dcp::LocalTime().as_string(),
843 Editor e (find_cpl("build/test/verify_test25"));
844 e.replace ("meta:Width", "meta:WidthX");
847 check_verify_result (
849 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
854 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
855 BOOST_AUTO_TEST_CASE (verify_test26)
857 boost::filesystem::path const dir("build/test/verify_test26");
858 prepare_directory (dir);
859 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
860 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
861 asset->_language = "wrong-andbad";
862 asset->write (dir / "subs.mxf");
863 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
864 reel_asset->_language = "badlang";
865 write_dcp_with_single_asset (dir, reel_asset);
867 check_verify_result (
870 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("badlang") },
871 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("wrong-andbad") },
872 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
877 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
878 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
880 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
881 prepare_directory (dir);
882 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
883 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
884 asset->_language = "wrong-andbad";
885 asset->write (dir / "subs.mxf");
886 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
887 reel_asset->_language = "badlang";
888 write_dcp_with_single_asset (dir, reel_asset);
890 check_verify_result (
893 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("badlang") },
894 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("wrong-andbad") },
895 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
900 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
901 * the release territory.
903 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
905 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
906 prepare_directory (dir);
908 auto picture = simple_picture (dir, "foo");
909 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
910 auto reel = make_shared<dcp::Reel>();
911 reel->add (reel_picture);
912 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
913 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
914 reel->add (reel_sound);
915 reel->add (simple_markers());
917 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
919 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
920 cpl->_additional_subtitle_languages.push_back("andso-is-this");
921 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
922 cpl->set_main_sound_sample_rate (48000);
923 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
924 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
925 cpl->set_version_number (1);
926 cpl->_release_territory = "fred-jim";
927 auto dcp = make_shared<dcp::DCP>(dir);
931 dcp::String::compose("libdcp %1", dcp::version),
932 dcp::String::compose("libdcp %1", dcp::version),
933 dcp::LocalTime().as_string(),
937 check_verify_result (
940 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("this-is-wrong") },
941 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("andso-is-this") },
942 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("fred-jim") },
943 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::BAD_LANGUAGE, string("frobozz") },
949 vector<dcp::VerificationNote>
950 check_picture_size (int width, int height, int frame_rate, bool three_d)
952 using namespace boost::filesystem;
954 path dcp_path = "build/test/verify_picture_test";
955 remove_all (dcp_path);
956 create_directories (dcp_path);
958 shared_ptr<dcp::PictureAsset> mp;
960 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
962 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
964 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
966 auto image = black_image (dcp::Size(width, height));
967 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
968 int const length = three_d ? frame_rate * 2 : frame_rate;
969 for (int i = 0; i < length; ++i) {
970 picture_writer->write (j2c.data(), j2c.size());
972 picture_writer->finalize ();
974 auto d = make_shared<dcp::DCP>(dcp_path);
975 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
976 cpl->set_annotation_text ("A Test DCP");
977 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
978 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
979 cpl->set_main_sound_sample_rate (48000);
980 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
981 cpl->set_main_picture_active_area (dcp::Size(1998, 1080));
982 cpl->set_version_number (1);
984 auto reel = make_shared<dcp::Reel>();
987 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
989 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
992 reel->add (simple_markers(frame_rate));
999 dcp::String::compose("libdcp %1", dcp::version),
1000 dcp::String::compose("libdcp %1", dcp::version),
1001 dcp::LocalTime().as_string(),
1005 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1011 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1013 auto notes = check_picture_size(width, height, frame_rate, three_d);
1015 BOOST_CHECK_EQUAL (notes.size(), 0U);
1021 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1023 auto notes = check_picture_size(width, height, frame_rate, three_d);
1024 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1025 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1026 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
1032 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1034 auto notes = check_picture_size(width, height, frame_rate, three_d);
1035 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1036 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1037 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
1043 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1045 auto notes = check_picture_size(width, height, frame_rate, three_d);
1046 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1047 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1048 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1052 BOOST_AUTO_TEST_CASE (verify_picture_size)
1054 using namespace boost::filesystem;
1057 check_picture_size_ok (2048, 858, 24, false);
1058 check_picture_size_ok (2048, 858, 25, false);
1059 check_picture_size_ok (2048, 858, 48, false);
1060 check_picture_size_ok (2048, 858, 24, true);
1061 check_picture_size_ok (2048, 858, 25, true);
1062 check_picture_size_ok (2048, 858, 48, true);
1065 check_picture_size_ok (1998, 1080, 24, false);
1066 check_picture_size_ok (1998, 1080, 25, false);
1067 check_picture_size_ok (1998, 1080, 48, false);
1068 check_picture_size_ok (1998, 1080, 24, true);
1069 check_picture_size_ok (1998, 1080, 25, true);
1070 check_picture_size_ok (1998, 1080, 48, true);
1073 check_picture_size_ok (4096, 1716, 24, false);
1076 check_picture_size_ok (3996, 2160, 24, false);
1078 /* Bad frame size */
1079 check_picture_size_bad_frame_size (2050, 858, 24, false);
1080 check_picture_size_bad_frame_size (2048, 658, 25, false);
1081 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1082 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1084 /* Bad 2K frame rate */
1085 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1086 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1087 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1089 /* Bad 4K frame rate */
1090 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1091 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1094 auto notes = check_picture_size(3996, 2160, 24, true);
1095 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1096 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1097 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1103 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1106 make_shared<dcp::SubtitleString>(
1114 dcp::Time(start_frame, 24, 24),
1115 dcp::Time(end_frame, 24, 24),
1131 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1133 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1134 prepare_directory (dir);
1136 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1137 for (int i = 0; i < 2048; ++i) {
1138 add_test_subtitle (asset, i * 24, i * 24 + 20);
1140 asset->set_language (dcp::LanguageTag("de-DE"));
1141 asset->write (dir / "subs.mxf");
1142 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1143 write_dcp_with_single_asset (dir, reel_asset);
1145 check_verify_result (
1148 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1149 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1150 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1151 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1157 shared_ptr<dcp::SMPTESubtitleAsset>
1158 make_large_subtitle_asset (boost::filesystem::path font_file)
1160 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1161 dcp::ArrayData big_fake_font(1024 * 1024);
1162 big_fake_font.write (font_file);
1163 for (int i = 0; i < 116; ++i) {
1164 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1172 verify_timed_text_asset_too_large (string name)
1174 auto const dir = boost::filesystem::path("build/test") / name;
1175 prepare_directory (dir);
1176 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1177 add_test_subtitle (asset, 0, 20);
1178 asset->set_language (dcp::LanguageTag("de-DE"));
1179 asset->write (dir / "subs.mxf");
1181 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1182 write_dcp_with_single_asset (dir, reel_asset);
1184 check_verify_result (
1187 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1188 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1189 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1190 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1191 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1196 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1198 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1199 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1203 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1205 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1206 prepare_directory (dir);
1207 auto dcp = make_simple (dir, 1, 240);
1210 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1211 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1212 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1213 "<ContentTitleText>Content</ContentTitleText>"
1214 "<AnnotationText>Annotation</AnnotationText>"
1215 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1216 "<ReelNumber>1</ReelNumber>"
1217 "<EditRate>25 1</EditRate>"
1218 "<TimeCodeRate>25</TimeCodeRate>"
1219 "<StartTime>00:00:00:00</StartTime>"
1220 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1222 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1223 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1224 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1230 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1231 BOOST_REQUIRE (xml_file);
1232 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1234 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1235 subs->write (dir / "subs.mxf");
1237 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1238 dcp->cpls().front()->reels().front()->add(reel_subs);
1241 dcp::String::compose("libdcp %1", dcp::version),
1242 dcp::String::compose("libdcp %1", dcp::version),
1243 dcp::LocalTime().as_string(),
1247 check_verify_result (
1250 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1251 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1256 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1258 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1259 auto dcp = make_simple (path, 2, 240);
1260 auto cpl = dcp->cpls()[0];
1263 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1264 subs->set_language (dcp::LanguageTag("de-DE"));
1265 subs->add (simple_subtitle());
1266 subs->write (path / "subs1.mxf");
1267 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1268 cpl->reels()[0]->add(reel_subs);
1272 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1273 subs->set_language (dcp::LanguageTag("en-US"));
1274 subs->add (simple_subtitle());
1275 subs->write (path / "subs2.mxf");
1276 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1277 cpl->reels()[1]->add(reel_subs);
1282 dcp::String::compose("libdcp %1", dcp::version),
1283 dcp::String::compose("libdcp %1", dcp::version),
1284 dcp::LocalTime().as_string(),
1288 check_verify_result (
1291 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1292 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1293 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1298 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1300 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1301 prepare_directory (dir);
1302 auto dcp = make_simple (dir, 1, 240);
1305 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1306 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1307 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1308 "<ContentTitleText>Content</ContentTitleText>"
1309 "<AnnotationText>Annotation</AnnotationText>"
1310 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1311 "<ReelNumber>1</ReelNumber>"
1312 "<Language>de-DE</Language>"
1313 "<EditRate>25 1</EditRate>"
1314 "<TimeCodeRate>25</TimeCodeRate>"
1315 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1317 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1318 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1319 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1325 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1326 BOOST_REQUIRE (xml_file);
1327 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1329 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1330 subs->write (dir / "subs.mxf");
1332 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1333 dcp->cpls().front()->reels().front()->add(reel_subs);
1336 dcp::String::compose("libdcp %1", dcp::version),
1337 dcp::String::compose("libdcp %1", dcp::version),
1338 dcp::LocalTime().as_string(),
1342 check_verify_result (
1345 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1346 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1351 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1353 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1354 prepare_directory (dir);
1355 auto dcp = make_simple (dir, 1, 240);
1358 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1359 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1360 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1361 "<ContentTitleText>Content</ContentTitleText>"
1362 "<AnnotationText>Annotation</AnnotationText>"
1363 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1364 "<ReelNumber>1</ReelNumber>"
1365 "<Language>de-DE</Language>"
1366 "<EditRate>25 1</EditRate>"
1367 "<TimeCodeRate>25</TimeCodeRate>"
1368 "<StartTime>00:00:02:00</StartTime>"
1369 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1371 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1372 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1373 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1379 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1380 BOOST_REQUIRE (xml_file);
1381 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1383 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1384 subs->write (dir / "subs.mxf");
1386 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1387 dcp->cpls().front()->reels().front()->add(reel_subs);
1390 dcp::String::compose("libdcp %1", dcp::version),
1391 dcp::String::compose("libdcp %1", dcp::version),
1392 dcp::LocalTime().as_string(),
1396 check_verify_result (
1399 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1400 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1408 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1411 , v_position(v_position_)
1424 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1426 prepare_directory (dir);
1427 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1428 asset->set_start_time (dcp::Time());
1429 for (auto i: subs) {
1430 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1432 asset->set_language (dcp::LanguageTag("de-DE"));
1433 asset->write (dir / "subs.mxf");
1435 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1436 write_dcp_with_single_asset (dir, reel_asset);
1440 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1442 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1443 /* Just too early */
1444 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1445 check_verify_result (
1448 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1449 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1455 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1457 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1458 /* Just late enough */
1459 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1460 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1464 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1466 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1467 prepare_directory (dir);
1469 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1470 asset1->set_start_time (dcp::Time());
1471 /* Just late enough */
1472 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1473 asset1->set_language (dcp::LanguageTag("de-DE"));
1474 asset1->write (dir / "subs1.mxf");
1475 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1476 auto reel1 = make_shared<dcp::Reel>();
1477 reel1->add (reel_asset1);
1478 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1479 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1480 reel1->add (markers1);
1482 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1483 asset2->set_start_time (dcp::Time());
1484 /* This would be too early on first reel but should be OK on the second */
1485 add_test_subtitle (asset2, 0, 4 * 24);
1486 asset2->set_language (dcp::LanguageTag("de-DE"));
1487 asset2->write (dir / "subs2.mxf");
1488 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1489 auto reel2 = make_shared<dcp::Reel>();
1490 reel2->add (reel_asset2);
1491 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1492 markers2->set (dcp::Marker::LFOC, dcp::Time(16 * 24 - 1, 24, 24));
1493 reel2->add (markers2);
1495 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1498 auto dcp = make_shared<dcp::DCP>(dir);
1502 dcp::String::compose("libdcp %1", dcp::version),
1503 dcp::String::compose("libdcp %1", dcp::version),
1504 dcp::LocalTime().as_string(),
1509 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1513 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1515 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1516 dcp_with_text<dcp::ReelSubtitleAsset> (
1520 { 5 * 24 + 1, 6 * 24 },
1522 check_verify_result (
1525 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE },
1526 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1531 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1533 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1534 dcp_with_text<dcp::ReelSubtitleAsset> (
1538 { 5 * 24 + 16, 8 * 24 },
1540 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1544 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1546 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1547 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1548 check_verify_result (
1551 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT },
1552 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1557 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1559 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1560 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1561 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1565 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1567 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1568 dcp_with_text<dcp::ReelSubtitleAsset> (
1571 { 96, 200, 0.0, "We" },
1572 { 96, 200, 0.1, "have" },
1573 { 96, 200, 0.2, "four" },
1574 { 96, 200, 0.3, "lines" }
1576 check_verify_result (
1579 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1580 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1585 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1587 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1588 dcp_with_text<dcp::ReelSubtitleAsset> (
1591 { 96, 200, 0.0, "We" },
1592 { 96, 200, 0.1, "have" },
1593 { 96, 200, 0.2, "four" },
1595 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1599 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1601 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1602 dcp_with_text<dcp::ReelSubtitleAsset> (
1605 { 96, 300, 0.0, "We" },
1606 { 96, 300, 0.1, "have" },
1607 { 150, 180, 0.2, "four" },
1608 { 150, 180, 0.3, "lines" }
1610 check_verify_result (
1613 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1614 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1619 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1621 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1622 dcp_with_text<dcp::ReelSubtitleAsset> (
1625 { 96, 300, 0.0, "We" },
1626 { 96, 300, 0.1, "have" },
1627 { 150, 180, 0.2, "four" },
1628 { 190, 250, 0.3, "lines" }
1630 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1634 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1636 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1637 dcp_with_text<dcp::ReelSubtitleAsset> (
1640 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1642 check_verify_result (
1645 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED },
1646 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1651 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1653 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1654 dcp_with_text<dcp::ReelSubtitleAsset> (
1657 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1659 check_verify_result (
1662 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG },
1663 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1668 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1670 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1671 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1674 { 96, 200, 0.0, "We" },
1675 { 96, 200, 0.1, "have" },
1676 { 96, 200, 0.2, "four" },
1677 { 96, 200, 0.3, "lines" }
1679 check_verify_result (
1682 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1683 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1688 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1690 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1691 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1694 { 96, 200, 0.0, "We" },
1695 { 96, 200, 0.1, "have" },
1696 { 96, 200, 0.2, "four" },
1698 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1702 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1704 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1705 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1708 { 96, 300, 0.0, "We" },
1709 { 96, 300, 0.1, "have" },
1710 { 150, 180, 0.2, "four" },
1711 { 150, 180, 0.3, "lines" }
1713 check_verify_result (
1716 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1717 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1722 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1724 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1725 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1728 { 96, 300, 0.0, "We" },
1729 { 96, 300, 0.1, "have" },
1730 { 150, 180, 0.2, "four" },
1731 { 190, 250, 0.3, "lines" }
1733 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1737 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1739 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1740 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1743 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1745 check_verify_result (
1748 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG },
1749 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1754 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1756 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1757 prepare_directory (dir);
1759 auto picture = simple_picture (dir, "foo");
1760 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1761 auto reel = make_shared<dcp::Reel>();
1762 reel->add (reel_picture);
1763 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1764 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1765 reel->add (reel_sound);
1766 reel->add (simple_markers());
1767 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1769 auto dcp = make_shared<dcp::DCP>(dir);
1773 dcp::String::compose("libdcp %1", dcp::version),
1774 dcp::String::compose("libdcp %1", dcp::version),
1775 dcp::LocalTime().as_string(),
1779 check_verify_result (
1782 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE },
1783 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1788 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1790 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1791 auto dcp = make_simple (dir);
1794 dcp::String::compose("libdcp %1", dcp::version),
1795 dcp::String::compose("libdcp %1", dcp::version),
1796 dcp::LocalTime().as_string(),
1800 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1803 BOOST_REQUIRE (dcp->cpls()[0]->file());
1804 Editor e(dcp->cpls()[0]->file().get());
1805 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1808 check_verify_result (
1811 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1812 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1817 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1819 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1820 auto dcp = make_simple (dir);
1823 dcp::String::compose("libdcp %1", dcp::version),
1824 dcp::String::compose("libdcp %1", dcp::version),
1825 dcp::LocalTime().as_string(),
1829 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1832 BOOST_REQUIRE (dcp->cpls()[0]->file());
1833 Editor e(dcp->cpls()[0]->file().get());
1834 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1837 check_verify_result (
1840 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1841 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1846 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1848 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1849 boost::filesystem::remove_all (dir);
1850 boost::filesystem::create_directories (dir);
1851 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1852 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1854 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1855 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1857 auto reel = make_shared<dcp::Reel>(
1858 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1859 make_shared<dcp::ReelSoundAsset>(ms, 0)
1862 reel->add (simple_markers());
1868 dcp::String::compose("libdcp %1", dcp::version),
1869 dcp::String::compose("libdcp %1", dcp::version),
1870 dcp::LocalTime().as_string(),
1874 check_verify_result (
1877 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION },
1878 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1886 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1888 boost::filesystem::remove_all (dir);
1889 boost::filesystem::create_directories (dir);
1890 auto dcp = make_shared<dcp::DCP>(dir);
1891 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1893 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1894 subs->set_language (dcp::LanguageTag("de-DE"));
1895 subs->set_start_time (dcp::Time());
1896 subs->add (simple_subtitle());
1897 subs->write (dir / "subs.mxf");
1898 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1900 auto reel1 = make_shared<dcp::Reel>(
1901 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1902 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1906 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1909 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1910 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1911 reel1->add (markers1);
1915 auto reel2 = make_shared<dcp::Reel>(
1916 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1917 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1921 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1924 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1925 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1926 reel2->add (markers2);
1933 dcp::String::compose("libdcp %1", dcp::version),
1934 dcp::String::compose("libdcp %1", dcp::version),
1935 dcp::LocalTime().as_string(),
1941 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1944 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1945 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1946 check_verify_result (
1949 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS },
1950 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1956 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1957 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1958 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1962 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1963 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1964 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1971 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1973 boost::filesystem::remove_all (dir);
1974 boost::filesystem::create_directories (dir);
1975 auto dcp = make_shared<dcp::DCP>(dir);
1976 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1978 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1979 subs->set_language (dcp::LanguageTag("de-DE"));
1980 subs->set_start_time (dcp::Time());
1981 subs->add (simple_subtitle());
1982 subs->write (dir / "subs.mxf");
1984 auto reel1 = make_shared<dcp::Reel>(
1985 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1986 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1989 for (int i = 0; i < caps_in_reel1; ++i) {
1990 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1993 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1994 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1995 reel1->add (markers1);
1999 auto reel2 = make_shared<dcp::Reel>(
2000 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2001 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2004 for (int i = 0; i < caps_in_reel2; ++i) {
2005 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2008 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
2009 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
2010 reel2->add (markers2);
2017 dcp::String::compose("libdcp %1", dcp::version),
2018 dcp::String::compose("libdcp %1", dcp::version),
2019 dcp::LocalTime().as_string(),
2025 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
2028 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
2029 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2030 check_verify_result (
2033 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER },
2034 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2039 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2040 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2041 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2045 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2046 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2047 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2054 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2056 boost::filesystem::remove_all (dir);
2057 boost::filesystem::create_directories (dir);
2058 auto dcp = make_shared<dcp::DCP>(dir);
2059 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2061 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2062 subs->set_language (dcp::LanguageTag("de-DE"));
2063 subs->set_start_time (dcp::Time());
2064 subs->add (simple_subtitle());
2065 subs->write (dir / "subs.mxf");
2066 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
2069 auto reel = make_shared<dcp::Reel>(
2070 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2071 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2074 reel->add (reel_text);
2076 reel->add (simple_markers(240));
2083 dcp::String::compose("libdcp %1", dcp::version),
2084 dcp::String::compose("libdcp %1", dcp::version),
2085 dcp::LocalTime().as_string(),
2089 check_verify_result (
2092 { dcp::VerificationNote::VERIFY_BV21_ERROR, code },
2093 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2098 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2100 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2101 "build/test/verify_subtitle_entry_point_must_be_present",
2102 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
2103 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2104 asset->unset_entry_point ();
2108 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2109 "build/test/verify_subtitle_entry_point_must_be_zero",
2110 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
2111 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2112 asset->set_entry_point (4);
2116 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2117 "build/test/verify_closed_caption_entry_point_must_be_present",
2118 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2119 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2120 asset->unset_entry_point ();
2124 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2125 "build/test/verify_closed_caption_entry_point_must_be_zero",
2126 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
2127 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2128 asset->set_entry_point (9);
2134 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
2138 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
2139 auto dcp = make_simple (dir);
2142 dcp::String::compose("libdcp %1", dcp::version),
2143 dcp::String::compose("libdcp %1", dcp::version),
2144 dcp::LocalTime().as_string(),
2148 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2151 BOOST_REQUIRE (dcp->cpls()[0]->file());
2152 Editor e(dcp->cpls()[0]->file().get());
2153 e.replace("<Hash>XGhFVrqZqapOJx5Fh2SLjj48Yjg=</Hash>", "");
2156 check_verify_result (
2159 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2160 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
2167 verify_markers_test (
2168 boost::filesystem::path dir,
2169 vector<pair<dcp::Marker, dcp::Time>> markers,
2170 vector<dcp::VerificationNote> test_notes
2173 auto dcp = make_simple (dir);
2174 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
2175 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24, 0);
2176 for (auto const& i: markers) {
2177 markers_asset->set (i.first, i.second);
2179 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2182 dcp::String::compose("libdcp %1", dcp::version),
2183 dcp::String::compose("libdcp %1", dcp::version),
2184 dcp::LocalTime().as_string(),
2188 check_verify_result ({dir}, test_notes);
2192 BOOST_AUTO_TEST_CASE (verify_markers)
2194 verify_markers_test (
2195 "build/test/verify_markers_all_correct",
2197 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2198 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2199 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2200 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2205 verify_markers_test (
2206 "build/test/verify_markers_missing_ffec",
2208 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2209 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2210 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2213 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2216 verify_markers_test (
2217 "build/test/verify_markers_missing_ffmc",
2219 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2220 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2221 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2224 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2227 verify_markers_test (
2228 "build/test/verify_markers_missing_ffoc",
2230 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2231 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2232 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2235 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2238 verify_markers_test (
2239 "build/test/verify_markers_missing_lfoc",
2241 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2242 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2243 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2246 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2249 verify_markers_test (
2250 "build/test/verify_markers_incorrect_ffoc",
2252 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2253 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2254 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2255 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2258 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2261 verify_markers_test (
2262 "build/test/verify_markers_incorrect_lfoc",
2264 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2265 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2266 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2267 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2270 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }
2275 BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
2277 boost::filesystem::path dir = "build/test/verify_cpl_metadata_version";
2278 prepare_directory (dir);
2279 auto dcp = make_simple (dir);
2280 dcp->cpls()[0]->unset_version_number();
2283 dcp::String::compose("libdcp %1", dcp::version),
2284 dcp::String::compose("libdcp %1", dcp::version),
2285 dcp::LocalTime().as_string(),
2289 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});
2293 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata1)
2295 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata1";
2296 auto dcp = make_simple (dir);
2299 dcp::String::compose("libdcp %1", dcp::version),
2300 dcp::String::compose("libdcp %1", dcp::version),
2301 dcp::LocalTime().as_string(),
2306 Editor e (dcp->cpls()[0]->file().get());
2307 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2310 check_verify_result (
2313 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2314 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2319 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata2)
2321 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata2";
2322 auto dcp = make_simple (dir);
2325 dcp::String::compose("libdcp %1", dcp::version),
2326 dcp::String::compose("libdcp %1", dcp::version),
2327 dcp::LocalTime().as_string(),
2332 Editor e (dcp->cpls()[0]->file().get());
2333 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2336 check_verify_result (
2339 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2340 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2345 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata3)
2347 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata3";
2348 auto dcp = make_simple (dir);
2351 dcp::String::compose("libdcp %1", dcp::version),
2352 dcp::String::compose("libdcp %1", dcp::version),
2353 dcp::LocalTime().as_string(),
2358 Editor e (dcp->cpls()[0]->file().get());
2359 e.replace ("<meta:Name>A", "<meta:NameX>A");
2360 e.replace ("n</meta:Name>", "n</meta:NameX>");
2363 check_verify_result (
2366 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2367 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2368 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2373 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata4)
2375 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata4";
2376 auto dcp = make_simple (dir);
2379 dcp::String::compose("libdcp %1", dcp::version),
2380 dcp::String::compose("libdcp %1", dcp::version),
2381 dcp::LocalTime().as_string(),
2386 Editor e (dcp->cpls()[0]->file().get());
2387 e.replace ("Application", "Fred");
2390 check_verify_result (
2393 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2394 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'Application'") },
2399 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata5)
2401 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata5";
2402 auto dcp = make_simple (dir);
2405 dcp::String::compose("libdcp %1", dcp::version),
2406 dcp::String::compose("libdcp %1", dcp::version),
2407 dcp::LocalTime().as_string(),
2411 Editor e (dcp->cpls()[0]->file().get());
2412 e.replace ("DCP Constraints Profile", "Fred");
2415 check_verify_result (
2418 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2419 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'") },
2424 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata6)
2426 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata6";
2427 auto dcp = make_simple (dir);
2430 dcp::String::compose("libdcp %1", dcp::version),
2431 dcp::String::compose("libdcp %1", dcp::version),
2432 dcp::LocalTime().as_string(),
2437 Editor e (dcp->cpls()[0]->file().get());
2438 e.replace ("<meta:Value>", "<meta:ValueX>");
2439 e.replace ("</meta:Value>", "</meta:ValueX>");
2442 check_verify_result (
2445 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2446 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2447 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2452 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata7)
2454 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata7";
2455 auto dcp = make_simple (dir);
2458 dcp::String::compose("libdcp %1", dcp::version),
2459 dcp::String::compose("libdcp %1", dcp::version),
2460 dcp::LocalTime().as_string(),
2464 Editor e (dcp->cpls()[0]->file().get());
2465 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2468 check_verify_result (
2471 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2472 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'") },
2477 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata8)
2479 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata8";
2480 auto dcp = make_simple (dir);
2483 dcp::String::compose("libdcp %1", dcp::version),
2484 dcp::String::compose("libdcp %1", dcp::version),
2485 dcp::LocalTime().as_string(),
2489 Editor e (dcp->cpls()[0]->file().get());
2490 e.replace ("<meta:Property>", "<meta:PropertyX>");
2491 e.replace ("</meta:Property>", "</meta:PropertyX>");
2494 check_verify_result (
2497 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2498 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2499 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2504 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata9)
2506 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata9";
2507 auto dcp = make_simple (dir);
2510 dcp::String::compose("libdcp %1", dcp::version),
2511 dcp::String::compose("libdcp %1", dcp::version),
2512 dcp::LocalTime().as_string(),
2516 Editor e (dcp->cpls()[0]->file().get());
2517 e.replace ("<meta:PropertyList>", "<meta:PropertyListX>");
2518 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2521 check_verify_result (
2524 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2525 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
2526 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2532 BOOST_AUTO_TEST_CASE (verify_encrypted_cpl_is_signed)
2534 boost::filesystem::path dir = "build/test/verify_encrypted_cpl_is_signed";
2535 prepare_directory (dir);
2536 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2537 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2541 Editor e (dir / "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
2542 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2545 check_verify_result (
2548 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2549 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_ANNOTATION_TEXT_DOES_NOT_MATCH_CPL_CONTENT_TITLE_TEXT },
2550 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
2551 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE },
2552 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC },
2553 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC },
2554 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_CPL_METADATA },
2555 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CPL_WITH_ENCRYPTED_CONTENT_NOT_SIGNED }
2560 BOOST_AUTO_TEST_CASE (verify_encrypted_pkl_is_signed)
2562 boost::filesystem::path dir = "build/test/verify_encrypted_pkl_is_signed";
2563 prepare_directory (dir);
2564 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2565 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2569 Editor e (dir / "pkl_93182bd2-b1e8-41a3-b5c8-6e6564273bff.xml");
2570 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2573 check_verify_result (
2576 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_ANNOTATION_TEXT_DOES_NOT_MATCH_CPL_CONTENT_TITLE_TEXT },
2577 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
2578 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE },
2579 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC },
2580 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC },
2581 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_CPL_METADATA },
2582 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PKL_WITH_ENCRYPTED_CONTENT_NOT_SIGNED }
2587 BOOST_AUTO_TEST_CASE (verify_unencrypted_pkl_can_be_unsigned)
2589 boost::filesystem::path dir = "build/test/verify_unencrypted_pkl_can_be_unsigned";
2590 prepare_directory (dir);
2591 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/dcp_test1")) {
2592 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2596 Editor e (dir / "pkl_2b9b857f-ab4a-440e-a313-1ace0f1cfc95.xml");
2597 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2600 check_verify_result ({dir}, {});
2604 BOOST_AUTO_TEST_CASE (verify_must_not_be_partially_encrypted)
2606 boost::filesystem::path dir ("build/test/verify_must_not_be_partially_encrypted");
2607 prepare_directory (dir);
2611 auto signer = make_shared<dcp::CertificateChain>();
2612 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
2613 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
2614 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
2615 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
2617 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2621 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::SMPTE);
2624 auto writer = mp->start_write (dir / "video.mxf", false);
2625 dcp::ArrayData j2c ("test/data/flat_red.j2c");
2626 for (int i = 0; i < 24; ++i) {
2627 writer->write (j2c.data(), j2c.size());
2629 writer->finalize ();
2631 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
2633 auto reel = make_shared<dcp::Reel>(
2634 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
2635 make_shared<dcp::ReelSoundAsset>(ms, 0)
2638 reel->add (simple_markers());
2642 cpl->set_content_version (
2643 {"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"}
2645 cpl->set_annotation_text ("A Test DCP");
2646 cpl->set_issuer ("OpenDCP 0.0.25");
2647 cpl->set_creator ("OpenDCP 0.0.25");
2648 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
2649 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
2650 cpl->set_main_sound_sample_rate (48000);
2651 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
2652 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
2653 cpl->set_version_number (1);
2657 d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "A Test DCP", signer);
2659 check_verify_result ({dir}, {{dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PARTIALLY_ENCRYPTED}});