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::INCORRECT_PICTURE_HASH },
303 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INCORRECT_SOUND_HASH }
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::MISMATCHED_CPL_HASHES },
321 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_PICTURE_HASHES },
322 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_SOUND_HASHES },
323 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
324 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
325 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML }
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::FAILED_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::MISMATCHED_CPL_HASHES,
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::INVALID_XML,
412 dcp::VerificationNote::INVALID_XML,
413 dcp::VerificationNote::INVALID_XML,
414 dcp::VerificationNote::INVALID_XML,
415 dcp::VerificationNote::INVALID_XML,
416 dcp::VerificationNote::MISMATCHED_CPL_HASHES }
420 /* Badly formatted <Id> in CPL */
421 BOOST_AUTO_TEST_CASE (verify_test9)
423 /* There's no MISMATCHED_CPL_HASHES 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::INVALID_XML }
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::INVALID_XML,
438 dcp::VerificationNote::MISMATCHED_CPL_HASHES }
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::INVALID_XML }
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::INVALID_XML }
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::INVALID_STANDARD);
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::INVALID_STANDARD },
525 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_DURATION },
526 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_INTRINSIC_DURATION },
527 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_DURATION },
528 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_INTRINSIC_DURATION }
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 prepare_directory (dir);
567 dcp_from_frame (oversized_frame, dir);
569 check_verify_result (
572 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_PICTURE_FRAME_SIZE_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 prepare_directory (dir);
595 dcp_from_frame (oversized_frame, dir);
597 check_verify_result (
600 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::NEARLY_INVALID_PICTURE_FRAME_SIZE_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 prepare_directory (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::INVALID_STANDARD }});
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::INVALID_STANDARD },
655 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
656 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML }
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::INVALID_XML },
689 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
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::INVALID_XML },
810 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
811 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
812 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES }
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::FAILED_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::INVALID_LANGUAGE, string("badlang") },
871 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_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::INVALID_LANGUAGE, string("badlang") },
894 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_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::INVALID_LANGUAGE, string("this-is-wrong") },
941 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_LANGUAGE, string("andso-is-this") },
942 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_LANGUAGE, string("fred-jim") },
943 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_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 prepare_directory (dcp_path);
957 shared_ptr<dcp::PictureAsset> mp;
959 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
961 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
963 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
965 auto image = black_image (dcp::Size(width, height));
966 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
967 int const length = three_d ? frame_rate * 2 : frame_rate;
968 for (int i = 0; i < length; ++i) {
969 picture_writer->write (j2c.data(), j2c.size());
971 picture_writer->finalize ();
973 auto d = make_shared<dcp::DCP>(dcp_path);
974 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
975 cpl->set_annotation_text ("A Test DCP");
976 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
977 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
978 cpl->set_main_sound_sample_rate (48000);
979 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
980 cpl->set_main_picture_active_area (dcp::Size(1998, 1080));
981 cpl->set_version_number (1);
983 auto reel = make_shared<dcp::Reel>();
986 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
988 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
991 reel->add (simple_markers(frame_rate));
998 dcp::String::compose("libdcp %1", dcp::version),
999 dcp::String::compose("libdcp %1", dcp::version),
1000 dcp::LocalTime().as_string(),
1004 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
1010 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1012 auto notes = check_picture_size(width, height, frame_rate, three_d);
1014 BOOST_CHECK_EQUAL (notes.size(), 0U);
1020 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1022 auto notes = check_picture_size(width, height, frame_rate, three_d);
1023 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1024 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1025 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::INVALID_PICTURE_SIZE_IN_PIXELS);
1031 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1033 auto notes = check_picture_size(width, height, frame_rate, three_d);
1034 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1035 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1036 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE_FOR_2K);
1042 check_picture_size_bad_4k_frame_rate (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::INVALID_PICTURE_FRAME_RATE_FOR_4K);
1051 BOOST_AUTO_TEST_CASE (verify_picture_size)
1053 using namespace boost::filesystem;
1056 check_picture_size_ok (2048, 858, 24, false);
1057 check_picture_size_ok (2048, 858, 25, false);
1058 check_picture_size_ok (2048, 858, 48, false);
1059 check_picture_size_ok (2048, 858, 24, true);
1060 check_picture_size_ok (2048, 858, 25, true);
1061 check_picture_size_ok (2048, 858, 48, true);
1064 check_picture_size_ok (1998, 1080, 24, false);
1065 check_picture_size_ok (1998, 1080, 25, false);
1066 check_picture_size_ok (1998, 1080, 48, false);
1067 check_picture_size_ok (1998, 1080, 24, true);
1068 check_picture_size_ok (1998, 1080, 25, true);
1069 check_picture_size_ok (1998, 1080, 48, true);
1072 check_picture_size_ok (4096, 1716, 24, false);
1075 check_picture_size_ok (3996, 2160, 24, false);
1077 /* Bad frame size */
1078 check_picture_size_bad_frame_size (2050, 858, 24, false);
1079 check_picture_size_bad_frame_size (2048, 658, 25, false);
1080 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1081 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1083 /* Bad 2K frame rate */
1084 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1085 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1086 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1088 /* Bad 4K frame rate */
1089 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1090 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1093 auto notes = check_picture_size(3996, 2160, 24, true);
1094 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1095 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1096 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::INVALID_PICTURE_ASSET_RESOLUTION_FOR_3D);
1102 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1105 make_shared<dcp::SubtitleString>(
1113 dcp::Time(start_frame, 24, 24),
1114 dcp::Time(end_frame, 24, 24),
1130 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1132 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1133 prepare_directory (dir);
1135 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1136 for (int i = 0; i < 2048; ++i) {
1137 add_test_subtitle (asset, i * 24, i * 24 + 20);
1139 asset->set_language (dcp::LanguageTag("de-DE"));
1140 asset->write (dir / "subs.mxf");
1141 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1142 write_dcp_with_single_asset (dir, reel_asset);
1144 check_verify_result (
1147 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1148 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_CLOSED_CAPTION_XML_SIZE_IN_BYTES },
1149 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1150 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1156 shared_ptr<dcp::SMPTESubtitleAsset>
1157 make_large_subtitle_asset (boost::filesystem::path font_file)
1159 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1160 dcp::ArrayData big_fake_font(1024 * 1024);
1161 big_fake_font.write (font_file);
1162 for (int i = 0; i < 116; ++i) {
1163 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1171 verify_timed_text_asset_too_large (string name)
1173 auto const dir = boost::filesystem::path("build/test") / name;
1174 prepare_directory (dir);
1175 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1176 add_test_subtitle (asset, 0, 20);
1177 asset->set_language (dcp::LanguageTag("de-DE"));
1178 asset->write (dir / "subs.mxf");
1180 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1181 write_dcp_with_single_asset (dir, reel_asset);
1183 check_verify_result (
1186 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_TIMED_TEXT_SIZE_IN_BYTES },
1187 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_TIMED_TEXT_FONT_SIZE_IN_BYTES },
1188 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1189 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1190 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1195 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1197 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1198 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1202 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1204 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1205 prepare_directory (dir);
1206 auto dcp = make_simple (dir, 1, 240);
1209 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1210 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1211 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1212 "<ContentTitleText>Content</ContentTitleText>"
1213 "<AnnotationText>Annotation</AnnotationText>"
1214 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1215 "<ReelNumber>1</ReelNumber>"
1216 "<EditRate>25 1</EditRate>"
1217 "<TimeCodeRate>25</TimeCodeRate>"
1218 "<StartTime>00:00:00:00</StartTime>"
1219 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1221 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1222 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1223 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1229 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1230 BOOST_REQUIRE (xml_file);
1231 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1233 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1234 subs->write (dir / "subs.mxf");
1236 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1237 dcp->cpls().front()->reels().front()->add(reel_subs);
1240 dcp::String::compose("libdcp %1", dcp::version),
1241 dcp::String::compose("libdcp %1", dcp::version),
1242 dcp::LocalTime().as_string(),
1246 check_verify_result (
1249 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1250 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1255 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1257 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1258 auto dcp = make_simple (path, 2, 240);
1259 auto cpl = dcp->cpls()[0];
1262 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1263 subs->set_language (dcp::LanguageTag("de-DE"));
1264 subs->add (simple_subtitle());
1265 subs->write (path / "subs1.mxf");
1266 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1267 cpl->reels()[0]->add(reel_subs);
1271 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1272 subs->set_language (dcp::LanguageTag("en-US"));
1273 subs->add (simple_subtitle());
1274 subs->write (path / "subs2.mxf");
1275 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1276 cpl->reels()[1]->add(reel_subs);
1281 dcp::String::compose("libdcp %1", dcp::version),
1282 dcp::String::compose("libdcp %1", dcp::version),
1283 dcp::LocalTime().as_string(),
1287 check_verify_result (
1290 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1291 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_SUBTITLE_LANGUAGES },
1292 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1297 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1299 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1300 prepare_directory (dir);
1301 auto dcp = make_simple (dir, 1, 240);
1304 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1305 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1306 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1307 "<ContentTitleText>Content</ContentTitleText>"
1308 "<AnnotationText>Annotation</AnnotationText>"
1309 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1310 "<ReelNumber>1</ReelNumber>"
1311 "<Language>de-DE</Language>"
1312 "<EditRate>25 1</EditRate>"
1313 "<TimeCodeRate>25</TimeCodeRate>"
1314 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1316 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1317 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1318 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1324 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1325 BOOST_REQUIRE (xml_file);
1326 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1328 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1329 subs->write (dir / "subs.mxf");
1331 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1332 dcp->cpls().front()->reels().front()->add(reel_subs);
1335 dcp::String::compose("libdcp %1", dcp::version),
1336 dcp::String::compose("libdcp %1", dcp::version),
1337 dcp::LocalTime().as_string(),
1341 check_verify_result (
1344 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1345 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1350 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1352 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1353 prepare_directory (dir);
1354 auto dcp = make_simple (dir, 1, 240);
1357 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1358 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1359 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1360 "<ContentTitleText>Content</ContentTitleText>"
1361 "<AnnotationText>Annotation</AnnotationText>"
1362 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1363 "<ReelNumber>1</ReelNumber>"
1364 "<Language>de-DE</Language>"
1365 "<EditRate>25 1</EditRate>"
1366 "<TimeCodeRate>25</TimeCodeRate>"
1367 "<StartTime>00:00:02:00</StartTime>"
1368 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1370 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1371 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1372 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1378 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1379 BOOST_REQUIRE (xml_file);
1380 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1382 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1383 subs->write (dir / "subs.mxf");
1385 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1386 dcp->cpls().front()->reels().front()->add(reel_subs);
1389 dcp::String::compose("libdcp %1", dcp::version),
1390 dcp::String::compose("libdcp %1", dcp::version),
1391 dcp::LocalTime().as_string(),
1395 check_verify_result (
1398 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SUBTITLE_START_TIME },
1399 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME }
1407 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1410 , v_position(v_position_)
1423 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1425 prepare_directory (dir);
1426 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1427 asset->set_start_time (dcp::Time());
1428 for (auto i: subs) {
1429 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1431 asset->set_language (dcp::LanguageTag("de-DE"));
1432 asset->write (dir / "subs.mxf");
1434 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1435 write_dcp_with_single_asset (dir, reel_asset);
1439 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1441 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1442 /* Just too early */
1443 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1444 check_verify_result (
1447 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_FIRST_TEXT_TIME },
1448 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1454 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1456 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1457 /* Just late enough */
1458 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1459 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1463 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1465 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1466 prepare_directory (dir);
1468 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1469 asset1->set_start_time (dcp::Time());
1470 /* Just late enough */
1471 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1472 asset1->set_language (dcp::LanguageTag("de-DE"));
1473 asset1->write (dir / "subs1.mxf");
1474 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1475 auto reel1 = make_shared<dcp::Reel>();
1476 reel1->add (reel_asset1);
1477 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1478 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1479 reel1->add (markers1);
1481 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1482 asset2->set_start_time (dcp::Time());
1483 /* This would be too early on first reel but should be OK on the second */
1484 add_test_subtitle (asset2, 0, 4 * 24);
1485 asset2->set_language (dcp::LanguageTag("de-DE"));
1486 asset2->write (dir / "subs2.mxf");
1487 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1488 auto reel2 = make_shared<dcp::Reel>();
1489 reel2->add (reel_asset2);
1490 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1491 markers2->set (dcp::Marker::LFOC, dcp::Time(16 * 24 - 1, 24, 24));
1492 reel2->add (markers2);
1494 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1497 auto dcp = make_shared<dcp::DCP>(dir);
1501 dcp::String::compose("libdcp %1", dcp::version),
1502 dcp::String::compose("libdcp %1", dcp::version),
1503 dcp::LocalTime().as_string(),
1508 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1512 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1514 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1515 dcp_with_text<dcp::ReelSubtitleAsset> (
1519 { 5 * 24 + 1, 6 * 24 },
1521 check_verify_result (
1524 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_SPACING },
1525 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1530 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1532 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1533 dcp_with_text<dcp::ReelSubtitleAsset> (
1537 { 5 * 24 + 16, 8 * 24 },
1539 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1543 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1545 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1546 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1547 check_verify_result (
1550 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_DURATION },
1551 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1556 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1558 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1559 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1560 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1564 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1566 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1567 dcp_with_text<dcp::ReelSubtitleAsset> (
1570 { 96, 200, 0.0, "We" },
1571 { 96, 200, 0.1, "have" },
1572 { 96, 200, 0.2, "four" },
1573 { 96, 200, 0.3, "lines" }
1575 check_verify_result (
1578 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_LINE_COUNT },
1579 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1584 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1586 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1587 dcp_with_text<dcp::ReelSubtitleAsset> (
1590 { 96, 200, 0.0, "We" },
1591 { 96, 200, 0.1, "have" },
1592 { 96, 200, 0.2, "four" },
1594 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1598 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1600 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1601 dcp_with_text<dcp::ReelSubtitleAsset> (
1604 { 96, 300, 0.0, "We" },
1605 { 96, 300, 0.1, "have" },
1606 { 150, 180, 0.2, "four" },
1607 { 150, 180, 0.3, "lines" }
1609 check_verify_result (
1612 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_LINE_COUNT },
1613 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1618 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1620 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1621 dcp_with_text<dcp::ReelSubtitleAsset> (
1624 { 96, 300, 0.0, "We" },
1625 { 96, 300, 0.1, "have" },
1626 { 150, 180, 0.2, "four" },
1627 { 190, 250, 0.3, "lines" }
1629 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1633 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1635 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1636 dcp_with_text<dcp::ReelSubtitleAsset> (
1639 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1641 check_verify_result (
1644 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::NEARLY_INVALID_SUBTITLE_LINE_LENGTH },
1645 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1650 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1652 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1653 dcp_with_text<dcp::ReelSubtitleAsset> (
1656 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1658 check_verify_result (
1661 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INVALID_SUBTITLE_LINE_LENGTH },
1662 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1667 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1669 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1670 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1673 { 96, 200, 0.0, "We" },
1674 { 96, 200, 0.1, "have" },
1675 { 96, 200, 0.2, "four" },
1676 { 96, 200, 0.3, "lines" }
1678 check_verify_result (
1681 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_CLOSED_CAPTION_LINE_COUNT},
1682 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1687 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1689 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1690 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1693 { 96, 200, 0.0, "We" },
1694 { 96, 200, 0.1, "have" },
1695 { 96, 200, 0.2, "four" },
1697 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1701 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1703 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1704 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1707 { 96, 300, 0.0, "We" },
1708 { 96, 300, 0.1, "have" },
1709 { 150, 180, 0.2, "four" },
1710 { 150, 180, 0.3, "lines" }
1712 check_verify_result (
1715 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_CLOSED_CAPTION_LINE_COUNT},
1716 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1721 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1723 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1724 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1727 { 96, 300, 0.0, "We" },
1728 { 96, 300, 0.1, "have" },
1729 { 150, 180, 0.2, "four" },
1730 { 190, 250, 0.3, "lines" }
1732 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1736 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1738 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1739 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1742 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1744 check_verify_result (
1747 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_CLOSED_CAPTION_LINE_LENGTH },
1748 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1753 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1755 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1756 prepare_directory (dir);
1758 auto picture = simple_picture (dir, "foo");
1759 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1760 auto reel = make_shared<dcp::Reel>();
1761 reel->add (reel_picture);
1762 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1763 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1764 reel->add (reel_sound);
1765 reel->add (simple_markers());
1766 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1768 auto dcp = make_shared<dcp::DCP>(dir);
1772 dcp::String::compose("libdcp %1", dcp::version),
1773 dcp::String::compose("libdcp %1", dcp::version),
1774 dcp::LocalTime().as_string(),
1778 check_verify_result (
1781 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE },
1782 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1787 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1789 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1790 auto dcp = make_simple (dir);
1793 dcp::String::compose("libdcp %1", dcp::version),
1794 dcp::String::compose("libdcp %1", dcp::version),
1795 dcp::LocalTime().as_string(),
1799 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1802 BOOST_REQUIRE (dcp->cpls()[0]->file());
1803 Editor e(dcp->cpls()[0]->file().get());
1804 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1807 check_verify_result (
1810 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_ANNOTATION_TEXT },
1811 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES }
1816 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1818 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1819 auto dcp = make_simple (dir);
1822 dcp::String::compose("libdcp %1", dcp::version),
1823 dcp::String::compose("libdcp %1", dcp::version),
1824 dcp::LocalTime().as_string(),
1828 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1831 BOOST_REQUIRE (dcp->cpls()[0]->file());
1832 Editor e(dcp->cpls()[0]->file().get());
1833 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1836 check_verify_result (
1839 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISMATCHED_CPL_ANNOTATION_TEXT },
1840 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES }
1845 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1847 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1848 prepare_directory (dir);
1849 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1850 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1852 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1853 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1855 auto reel = make_shared<dcp::Reel>(
1856 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1857 make_shared<dcp::ReelSoundAsset>(ms, 0)
1860 reel->add (simple_markers());
1866 dcp::String::compose("libdcp %1", dcp::version),
1867 dcp::String::compose("libdcp %1", dcp::version),
1868 dcp::LocalTime().as_string(),
1872 check_verify_result (
1875 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION },
1876 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1884 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1886 prepare_directory (dir);
1887 auto dcp = make_shared<dcp::DCP>(dir);
1888 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1890 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1891 subs->set_language (dcp::LanguageTag("de-DE"));
1892 subs->set_start_time (dcp::Time());
1893 subs->add (simple_subtitle());
1894 subs->write (dir / "subs.mxf");
1895 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1897 auto reel1 = make_shared<dcp::Reel>(
1898 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1899 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1903 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1906 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1907 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1908 reel1->add (markers1);
1912 auto reel2 = make_shared<dcp::Reel>(
1913 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1914 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1918 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1921 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1922 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1923 reel2->add (markers2);
1930 dcp::String::compose("libdcp %1", dcp::version),
1931 dcp::String::compose("libdcp %1", dcp::version),
1932 dcp::LocalTime().as_string(),
1938 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1941 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1942 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1943 check_verify_result (
1946 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_MAIN_SUBTITLE_FROM_SOME_REELS },
1947 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1953 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1954 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1955 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1959 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1960 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1961 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1968 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1970 prepare_directory (dir);
1971 auto dcp = make_shared<dcp::DCP>(dir);
1972 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1974 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1975 subs->set_language (dcp::LanguageTag("de-DE"));
1976 subs->set_start_time (dcp::Time());
1977 subs->add (simple_subtitle());
1978 subs->write (dir / "subs.mxf");
1980 auto reel1 = make_shared<dcp::Reel>(
1981 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1982 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1985 for (int i = 0; i < caps_in_reel1; ++i) {
1986 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1989 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1990 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1991 reel1->add (markers1);
1995 auto reel2 = make_shared<dcp::Reel>(
1996 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1997 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2000 for (int i = 0; i < caps_in_reel2; ++i) {
2001 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
2004 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
2005 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
2006 reel2->add (markers2);
2013 dcp::String::compose("libdcp %1", dcp::version),
2014 dcp::String::compose("libdcp %1", dcp::version),
2015 dcp::LocalTime().as_string(),
2021 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
2024 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
2025 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
2026 check_verify_result (
2029 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_CLOSED_CAPTION_ASSET_COUNTS },
2030 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2035 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
2036 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
2037 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2041 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
2042 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
2043 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
2050 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
2052 prepare_directory (dir);
2053 auto dcp = make_shared<dcp::DCP>(dir);
2054 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2056 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
2057 subs->set_language (dcp::LanguageTag("de-DE"));
2058 subs->set_start_time (dcp::Time());
2059 subs->add (simple_subtitle());
2060 subs->write (dir / "subs.mxf");
2061 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
2064 auto reel = make_shared<dcp::Reel>(
2065 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
2066 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
2069 reel->add (reel_text);
2071 reel->add (simple_markers(240));
2078 dcp::String::compose("libdcp %1", dcp::version),
2079 dcp::String::compose("libdcp %1", dcp::version),
2080 dcp::LocalTime().as_string(),
2084 check_verify_result (
2087 { dcp::VerificationNote::VERIFY_BV21_ERROR, code },
2088 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
2093 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
2095 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2096 "build/test/verify_subtitle_entry_point_must_be_present",
2097 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
2098 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2099 asset->unset_entry_point ();
2103 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2104 "build/test/verify_subtitle_entry_point_must_be_zero",
2105 dcp::VerificationNote::INCORRECT_SUBTITLE_ENTRY_POINT,
2106 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2107 asset->set_entry_point (4);
2111 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2112 "build/test/verify_closed_caption_entry_point_must_be_present",
2113 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2114 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2115 asset->unset_entry_point ();
2119 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2120 "build/test/verify_closed_caption_entry_point_must_be_zero",
2121 dcp::VerificationNote::INCORRECT_CLOSED_CAPTION_ENTRY_POINT,
2122 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2123 asset->set_entry_point (9);
2129 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
2133 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
2134 auto dcp = make_simple (dir);
2137 dcp::String::compose("libdcp %1", dcp::version),
2138 dcp::String::compose("libdcp %1", dcp::version),
2139 dcp::LocalTime().as_string(),
2143 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2146 BOOST_REQUIRE (dcp->cpls()[0]->file());
2147 Editor e(dcp->cpls()[0]->file().get());
2148 e.replace("<Hash>XGhFVrqZqapOJx5Fh2SLjj48Yjg=</Hash>", "");
2151 check_verify_result (
2154 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2155 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
2162 verify_markers_test (
2163 boost::filesystem::path dir,
2164 vector<pair<dcp::Marker, dcp::Time>> markers,
2165 vector<dcp::VerificationNote> test_notes
2168 auto dcp = make_simple (dir);
2169 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
2170 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24, 0);
2171 for (auto const& i: markers) {
2172 markers_asset->set (i.first, i.second);
2174 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2177 dcp::String::compose("libdcp %1", dcp::version),
2178 dcp::String::compose("libdcp %1", dcp::version),
2179 dcp::LocalTime().as_string(),
2183 check_verify_result ({dir}, test_notes);
2187 BOOST_AUTO_TEST_CASE (verify_markers)
2189 verify_markers_test (
2190 "build/test/verify_markers_all_correct",
2192 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2193 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2194 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2195 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2200 verify_markers_test (
2201 "build/test/verify_markers_missing_ffec",
2203 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2204 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2205 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2208 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2211 verify_markers_test (
2212 "build/test/verify_markers_missing_ffmc",
2214 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2215 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2216 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2219 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2222 verify_markers_test (
2223 "build/test/verify_markers_missing_ffoc",
2225 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2226 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2227 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2230 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2233 verify_markers_test (
2234 "build/test/verify_markers_missing_lfoc",
2236 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2237 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2238 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2241 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2244 verify_markers_test (
2245 "build/test/verify_markers_incorrect_ffoc",
2247 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2248 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2249 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2250 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2253 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2256 verify_markers_test (
2257 "build/test/verify_markers_incorrect_lfoc",
2259 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2260 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2261 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2262 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2265 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }
2270 BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
2272 boost::filesystem::path dir = "build/test/verify_cpl_metadata_version";
2273 prepare_directory (dir);
2274 auto dcp = make_simple (dir);
2275 dcp->cpls()[0]->unset_version_number();
2278 dcp::String::compose("libdcp %1", dcp::version),
2279 dcp::String::compose("libdcp %1", dcp::version),
2280 dcp::LocalTime().as_string(),
2284 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});
2288 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata1)
2290 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata1";
2291 auto dcp = make_simple (dir);
2294 dcp::String::compose("libdcp %1", dcp::version),
2295 dcp::String::compose("libdcp %1", dcp::version),
2296 dcp::LocalTime().as_string(),
2301 Editor e (dcp->cpls()[0]->file().get());
2302 e.delete_lines ("<meta:ExtensionMetadataList>", "</meta:ExtensionMetadataList>");
2305 check_verify_result (
2308 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2309 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2314 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata2)
2316 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata2";
2317 auto dcp = make_simple (dir);
2320 dcp::String::compose("libdcp %1", dcp::version),
2321 dcp::String::compose("libdcp %1", dcp::version),
2322 dcp::LocalTime().as_string(),
2327 Editor e (dcp->cpls()[0]->file().get());
2328 e.delete_lines ("<meta:ExtensionMetadata scope=\"http://isdcf.com/ns/cplmd/app\">", "</meta:ExtensionMetadata>");
2331 check_verify_result (
2334 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2335 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_EXTENSION_METADATA }
2340 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata3)
2342 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata3";
2343 auto dcp = make_simple (dir);
2346 dcp::String::compose("libdcp %1", dcp::version),
2347 dcp::String::compose("libdcp %1", dcp::version),
2348 dcp::LocalTime().as_string(),
2353 Editor e (dcp->cpls()[0]->file().get());
2354 e.replace ("<meta:Name>A", "<meta:NameX>A");
2355 e.replace ("n</meta:Name>", "n</meta:NameX>");
2358 check_verify_result (
2361 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2362 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2363 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2368 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata4)
2370 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata4";
2371 auto dcp = make_simple (dir);
2374 dcp::String::compose("libdcp %1", dcp::version),
2375 dcp::String::compose("libdcp %1", dcp::version),
2376 dcp::LocalTime().as_string(),
2381 Editor e (dcp->cpls()[0]->file().get());
2382 e.replace ("Application", "Fred");
2385 check_verify_result (
2388 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2389 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'Application'") },
2394 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata5)
2396 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata5";
2397 auto dcp = make_simple (dir);
2400 dcp::String::compose("libdcp %1", dcp::version),
2401 dcp::String::compose("libdcp %1", dcp::version),
2402 dcp::LocalTime().as_string(),
2406 Editor e (dcp->cpls()[0]->file().get());
2407 e.replace ("DCP Constraints Profile", "Fred");
2410 check_verify_result (
2413 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2414 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Name> property should be 'DCP Constraints Profile'") },
2419 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata6)
2421 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata6";
2422 auto dcp = make_simple (dir);
2425 dcp::String::compose("libdcp %1", dcp::version),
2426 dcp::String::compose("libdcp %1", dcp::version),
2427 dcp::LocalTime().as_string(),
2432 Editor e (dcp->cpls()[0]->file().get());
2433 e.replace ("<meta:Value>", "<meta:ValueX>");
2434 e.replace ("</meta:Value>", "</meta:ValueX>");
2437 check_verify_result (
2440 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2441 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2442 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2447 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata7)
2449 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata7";
2450 auto dcp = make_simple (dir);
2453 dcp::String::compose("libdcp %1", dcp::version),
2454 dcp::String::compose("libdcp %1", dcp::version),
2455 dcp::LocalTime().as_string(),
2459 Editor e (dcp->cpls()[0]->file().get());
2460 e.replace ("SMPTE-RDD-52:2020-Bv2.1", "Fred");
2463 check_verify_result (
2466 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2467 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_EXTENSION_METADATA, string("<Value> property should be 'SMPTE-RDD-52:2020-Bv2.1'") },
2472 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata8)
2474 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata8";
2475 auto dcp = make_simple (dir);
2478 dcp::String::compose("libdcp %1", dcp::version),
2479 dcp::String::compose("libdcp %1", dcp::version),
2480 dcp::LocalTime().as_string(),
2484 Editor e (dcp->cpls()[0]->file().get());
2485 e.replace ("<meta:Property>", "<meta:PropertyX>");
2486 e.replace ("</meta:Property>", "</meta:PropertyX>");
2489 check_verify_result (
2492 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2493 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2494 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2499 BOOST_AUTO_TEST_CASE (verify_cpl_extension_metadata9)
2501 boost::filesystem::path dir = "build/test/verify_cpl_extension_metadata9";
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:PropertyList>", "<meta:PropertyListX>");
2513 e.replace ("</meta:PropertyList>", "</meta:PropertyListX>");
2516 check_verify_result (
2519 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2520 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INVALID_XML },
2521 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2527 BOOST_AUTO_TEST_CASE (verify_encrypted_cpl_is_signed)
2529 boost::filesystem::path dir = "build/test/verify_encrypted_cpl_is_signed";
2530 prepare_directory (dir);
2531 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2532 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2536 Editor e (dir / "cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
2537 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2540 check_verify_result (
2543 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISMATCHED_CPL_HASHES },
2544 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT },
2545 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
2546 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE },
2547 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC },
2548 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC },
2549 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_CPL_METADATA },
2550 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::UNSIGNED_CPL_WITH_ENCRYPTED_CONTENT }
2555 BOOST_AUTO_TEST_CASE (verify_encrypted_pkl_is_signed)
2557 boost::filesystem::path dir = "build/test/verify_encrypted_pkl_is_signed";
2558 prepare_directory (dir);
2559 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/encryption_test")) {
2560 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2564 Editor e (dir / "pkl_93182bd2-b1e8-41a3-b5c8-6e6564273bff.xml");
2565 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2568 check_verify_result (
2571 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT },
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::UNSIGNED_PKL_WITH_ENCRYPTED_CONTENT }
2582 BOOST_AUTO_TEST_CASE (verify_unencrypted_pkl_can_be_unsigned)
2584 boost::filesystem::path dir = "build/test/verify_unencrypted_pkl_can_be_unsigned";
2585 prepare_directory (dir);
2586 for (auto i: boost::filesystem::directory_iterator("test/ref/DCP/dcp_test1")) {
2587 boost::filesystem::copy_file (i.path(), dir / i.path().filename());
2591 Editor e (dir / "pkl_2b9b857f-ab4a-440e-a313-1ace0f1cfc95.xml");
2592 e.delete_lines ("<dsig:Signature", "</dsig:Signature>");
2595 check_verify_result ({dir}, {});
2599 BOOST_AUTO_TEST_CASE (verify_must_not_be_partially_encrypted)
2601 boost::filesystem::path dir ("build/test/verify_must_not_be_partially_encrypted");
2602 prepare_directory (dir);
2606 auto signer = make_shared<dcp::CertificateChain>();
2607 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/ca.self-signed.pem")));
2608 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/intermediate.signed.pem")));
2609 signer->add (dcp::Certificate(dcp::file_to_string("test/ref/crypt/leaf.signed.pem")));
2610 signer->set_key (dcp::file_to_string("test/ref/crypt/leaf.key"));
2612 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
2616 auto mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction (24, 1), dcp::SMPTE);
2619 auto writer = mp->start_write (dir / "video.mxf", false);
2620 dcp::ArrayData j2c ("test/data/flat_red.j2c");
2621 for (int i = 0; i < 24; ++i) {
2622 writer->write (j2c.data(), j2c.size());
2624 writer->finalize ();
2626 auto ms = simple_sound (dir, "", dcp::MXFMetadata(), "de-DE");
2628 auto reel = make_shared<dcp::Reel>(
2629 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
2630 make_shared<dcp::ReelSoundAsset>(ms, 0)
2633 reel->add (simple_markers());
2637 cpl->set_content_version (
2638 {"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"}
2640 cpl->set_annotation_text ("A Test DCP");
2641 cpl->set_issuer ("OpenDCP 0.0.25");
2642 cpl->set_creator ("OpenDCP 0.0.25");
2643 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
2644 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
2645 cpl->set_main_sound_sample_rate (48000);
2646 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
2647 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
2648 cpl->set_version_number (1);
2652 d.write_xml (dcp::SMPTE, "OpenDCP 0.0.25", "OpenDCP 0.0.25", "2012-07-17T04:45:18+00:00", "A Test DCP", signer);
2654 check_verify_result ({dir}, {{dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::PARTIALLY_ENCRYPTED}});