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);
120 dcp->write_xml (standard);
124 /** Class that can alter a file by searching and replacing strings within it.
125 * On destruction modifies the file whose name was given to the constructor.
130 Editor (boost::filesystem::path path)
133 _content = dcp::file_to_string (_path);
138 auto f = fopen(_path.string().c_str(), "w");
140 fwrite (_content.c_str(), _content.length(), 1, f);
144 void replace (string a, string b)
146 auto old_content = _content;
147 boost::algorithm::replace_all (_content, a, b);
148 BOOST_REQUIRE (_content != old_content);
152 boost::filesystem::path _path;
153 std::string _content;
159 dump_notes (vector<dcp::VerificationNote> const & notes)
161 for (auto i: notes) {
162 std::cout << dcp::note_to_string(i) << "\n";
169 check_verify_result (vector<boost::filesystem::path> dir, vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes)
171 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
172 BOOST_REQUIRE_EQUAL (notes.size(), types_and_codes.size());
173 auto i = notes.begin();
174 auto j = types_and_codes.begin();
175 while (i != notes.end()) {
176 BOOST_CHECK_EQUAL (i->type(), j->first);
177 BOOST_CHECK_EQUAL (i->code(), j->second);
186 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
188 auto directories = setup (1, n);
192 e.replace (from, to);
195 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
197 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
198 auto i = notes.begin();
199 auto j = codes.begin();
200 while (i != notes.end()) {
201 BOOST_CHECK_EQUAL (i->code(), *j);
208 /* Check DCP as-is (should be OK) */
209 BOOST_AUTO_TEST_CASE (verify_test1)
212 auto directories = setup (1, 1);
213 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
215 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test1" / dcp_test1_cpl;
216 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test1" / dcp_test1_pkl;
217 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
219 auto st = stages.begin();
220 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
221 BOOST_REQUIRE (st->second);
222 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
224 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
225 BOOST_REQUIRE (st->second);
226 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
228 BOOST_CHECK_EQUAL (st->first, "Checking reel");
229 BOOST_REQUIRE (!st->second);
231 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
232 BOOST_REQUIRE (st->second);
233 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
235 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
236 BOOST_REQUIRE (st->second);
237 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
239 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
240 BOOST_REQUIRE (st->second);
241 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
243 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
244 BOOST_REQUIRE (st->second);
245 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
247 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
248 BOOST_REQUIRE (st->second);
249 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
251 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
252 BOOST_REQUIRE (st->second);
253 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
255 BOOST_REQUIRE (st == stages.end());
257 BOOST_CHECK_EQUAL (notes.size(), 0);
260 /* Corrupt the MXFs and check that this is spotted */
261 BOOST_AUTO_TEST_CASE (verify_test2)
263 auto directories = setup (1, 2);
265 auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
267 fseek (mod, 4096, SEEK_SET);
269 fwrite (&x, sizeof(x), 1, mod);
272 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
274 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
275 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
278 dcp::ASDCPErrorSuspender sus;
279 check_verify_result (
282 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
283 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
287 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
288 BOOST_AUTO_TEST_CASE (verify_test3)
290 auto directories = setup (1, 3);
293 Editor e (boost::filesystem::path("build") / "test" / "verify_test3" / dcp_test1_pkl);
294 e.replace ("<Hash>", "<Hash>x");
297 check_verify_result (
300 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
301 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER },
302 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER },
303 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
304 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
305 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
309 /* Corrupt the ContentKind in the CPL */
310 BOOST_AUTO_TEST_CASE (verify_test4)
312 auto directories = setup (1, 4);
315 Editor e (boost::filesystem::path("build") / "test" / "verify_test4" / dcp_test1_cpl);
316 e.replace ("<ContentKind>", "<ContentKind>x");
319 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
321 BOOST_REQUIRE_EQUAL (notes.size(), 1);
322 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
323 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xtrailer'");
327 boost::filesystem::path
330 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_cpl);
334 boost::filesystem::path
337 return dcp::String::compose("build/test/verify_test%1/%2", n, dcp_test1_pkl);
341 boost::filesystem::path
344 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
349 BOOST_AUTO_TEST_CASE (verify_test5)
351 check_verify_result_after_replace (
353 "<FrameRate>24 1", "<FrameRate>99 1",
354 { dcp::VerificationNote::CPL_HASH_INCORRECT,
355 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
360 BOOST_AUTO_TEST_CASE (verify_test6)
362 auto directories = setup (1, 6);
364 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
365 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
369 boost::filesystem::path
372 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
375 /* Empty asset filename in ASSETMAP */
376 BOOST_AUTO_TEST_CASE (verify_test7)
378 check_verify_result_after_replace (
380 "<Path>video.mxf</Path>", "<Path></Path>",
381 { dcp::VerificationNote::EMPTY_ASSET_PATH }
385 /* Mismatched standard */
386 BOOST_AUTO_TEST_CASE (verify_test8)
388 check_verify_result_after_replace (
390 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
391 { dcp::VerificationNote::MISMATCHED_STANDARD,
392 dcp::VerificationNote::XML_VALIDATION_ERROR,
393 dcp::VerificationNote::XML_VALIDATION_ERROR,
394 dcp::VerificationNote::XML_VALIDATION_ERROR,
395 dcp::VerificationNote::XML_VALIDATION_ERROR,
396 dcp::VerificationNote::XML_VALIDATION_ERROR,
397 dcp::VerificationNote::CPL_HASH_INCORRECT }
401 /* Badly formatted <Id> in CPL */
402 BOOST_AUTO_TEST_CASE (verify_test9)
404 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
405 check_verify_result_after_replace (
407 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
408 { dcp::VerificationNote::XML_VALIDATION_ERROR }
412 /* Badly formatted <IssueDate> in CPL */
413 BOOST_AUTO_TEST_CASE (verify_test10)
415 check_verify_result_after_replace (
417 "<IssueDate>", "<IssueDate>x",
418 { dcp::VerificationNote::XML_VALIDATION_ERROR,
419 dcp::VerificationNote::CPL_HASH_INCORRECT }
423 /* Badly-formatted <Id> in PKL */
424 BOOST_AUTO_TEST_CASE (verify_test11)
426 check_verify_result_after_replace (
428 "<Id>urn:uuid:2b9", "<Id>urn:uuid:xb9",
429 { dcp::VerificationNote::XML_VALIDATION_ERROR }
433 /* Badly-formatted <Id> in ASSETMAP */
434 BOOST_AUTO_TEST_CASE (verify_test12)
436 check_verify_result_after_replace (
438 "<Id>urn:uuid:07e", "<Id>urn:uuid:x7e",
439 { dcp::VerificationNote::XML_VALIDATION_ERROR }
443 /* Basic test of an Interop DCP */
444 BOOST_AUTO_TEST_CASE (verify_test13)
447 auto directories = setup (3, 13);
448 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
450 boost::filesystem::path const cpl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
451 boost::filesystem::path const pkl_file = boost::filesystem::path("build") / "test" / "verify_test13" / "pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
452 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
454 auto st = stages.begin();
455 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
456 BOOST_REQUIRE (st->second);
457 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
459 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
460 BOOST_REQUIRE (st->second);
461 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
463 BOOST_CHECK_EQUAL (st->first, "Checking reel");
464 BOOST_REQUIRE (!st->second);
466 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
467 BOOST_REQUIRE (st->second);
468 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
470 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
471 BOOST_REQUIRE (st->second);
472 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
474 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
475 BOOST_REQUIRE (st->second);
476 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
478 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
479 BOOST_REQUIRE (st->second);
480 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
482 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
483 BOOST_REQUIRE (st->second);
484 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
486 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
487 BOOST_REQUIRE (st->second);
488 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
490 BOOST_REQUIRE (st == stages.end());
492 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
493 auto i = notes.begin ();
494 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
495 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
498 /* DCP with a short asset */
499 BOOST_AUTO_TEST_CASE (verify_test14)
501 auto directories = setup (8, 14);
502 check_verify_result (
505 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
506 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
507 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
508 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
509 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
516 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
518 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
519 boost::filesystem::create_directories (dir);
520 auto writer = asset->start_write (dir / "pic.mxf", true);
521 for (int i = 0; i < 24; ++i) {
522 writer->write (frame.data(), frame.size());
526 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
527 write_dcp_with_single_asset (dir, reel_asset);
531 /* DCP with an over-sized JPEG2000 frame */
532 BOOST_AUTO_TEST_CASE (verify_test15)
534 int const too_big = 1302083 * 2;
536 /* Compress a black image */
537 auto image = black_image ();
538 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
539 BOOST_REQUIRE (frame.size() < too_big);
541 /* Place it in a bigger block with some zero padding at the end */
542 dcp::ArrayData oversized_frame(too_big);
543 memcpy (oversized_frame.data(), frame.data(), frame.size());
544 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
546 boost::filesystem::path const dir("build/test/verify_test15");
547 boost::filesystem::remove_all (dir);
548 dcp_from_frame (oversized_frame, dir);
550 check_verify_result (
553 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES },
554 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
559 /* DCP with a nearly over-sized JPEG2000 frame */
560 BOOST_AUTO_TEST_CASE (verify_test16)
562 int const nearly_too_big = 1302083 * 0.98;
564 /* Compress a black image */
565 auto image = black_image ();
566 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
567 BOOST_REQUIRE (frame.size() < nearly_too_big);
569 /* Place it in a bigger block with some zero padding at the end */
570 dcp::ArrayData oversized_frame(nearly_too_big);
571 memcpy (oversized_frame.data(), frame.data(), frame.size());
572 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
574 boost::filesystem::path const dir("build/test/verify_test16");
575 boost::filesystem::remove_all (dir);
576 dcp_from_frame (oversized_frame, dir);
578 check_verify_result (
581 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES },
582 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
587 /* DCP with a within-range JPEG2000 frame */
588 BOOST_AUTO_TEST_CASE (verify_test17)
590 /* Compress a black image */
591 auto image = black_image ();
592 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
593 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
595 boost::filesystem::path const dir("build/test/verify_test17");
596 boost::filesystem::remove_all (dir);
597 dcp_from_frame (frame, dir);
599 check_verify_result ({ dir }, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
603 /* DCP with valid Interop subtitles */
604 BOOST_AUTO_TEST_CASE (verify_test18)
606 boost::filesystem::path const dir("build/test/verify_test18");
607 prepare_directory (dir);
608 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
609 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
610 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
611 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
613 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
617 /* DCP with broken Interop subtitles */
618 BOOST_AUTO_TEST_CASE (verify_test19)
620 boost::filesystem::path const dir("build/test/verify_test19");
621 prepare_directory (dir);
622 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
623 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
624 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
625 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
628 Editor e (dir / "subs.xml");
629 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
632 check_verify_result (
635 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
636 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
637 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
642 /* DCP with valid SMPTE subtitles */
643 BOOST_AUTO_TEST_CASE (verify_test20)
645 boost::filesystem::path const dir("build/test/verify_test20");
646 prepare_directory (dir);
647 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
648 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
649 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
650 write_dcp_with_single_asset (dir, reel_asset);
652 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
656 /* DCP with broken SMPTE subtitles */
657 BOOST_AUTO_TEST_CASE (verify_test21)
659 boost::filesystem::path const dir("build/test/verify_test21");
660 prepare_directory (dir);
661 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
662 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
663 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
664 write_dcp_with_single_asset (dir, reel_asset);
666 check_verify_result (
669 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
670 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
671 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
672 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
678 BOOST_AUTO_TEST_CASE (verify_test22)
680 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
681 prepare_directory (ov_dir);
683 auto image = black_image ();
684 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
685 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
686 dcp_from_frame (frame, ov_dir);
688 dcp::DCP ov (ov_dir);
691 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
692 prepare_directory (vf_dir);
694 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
696 check_verify_result (
699 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET },
700 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
705 /* DCP with valid CompositionMetadataAsset */
706 BOOST_AUTO_TEST_CASE (verify_test23)
708 boost::filesystem::path const dir("build/test/verify_test23");
709 prepare_directory (dir);
711 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
712 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
713 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
715 auto reel = make_shared<dcp::Reel>();
716 reel->add (reel_asset);
718 reel->add (simple_markers(16 * 24 - 1));
720 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
722 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
723 cpl->set_main_sound_sample_rate (48000);
724 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
725 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
729 dcp.write_xml (dcp::SMPTE);
731 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
735 boost::filesystem::path find_cpl (boost::filesystem::path dir)
737 for (auto i: boost::filesystem::directory_iterator(dir)) {
738 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
743 BOOST_REQUIRE (false);
748 /* DCP with invalid CompositionMetadataAsset */
749 BOOST_AUTO_TEST_CASE (verify_test24)
751 boost::filesystem::path const dir("build/test/verify_test24");
752 prepare_directory (dir);
754 auto reel = make_shared<dcp::Reel>();
755 reel->add (black_picture_asset(dir));
756 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
758 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
759 cpl->set_main_sound_sample_rate (48000);
760 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
761 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
762 cpl->set_version_number (1);
764 reel->add (simple_markers());
768 dcp.write_xml (dcp::SMPTE);
771 Editor e (find_cpl("build/test/verify_test24"));
772 e.replace ("MainSound", "MainSoundX");
775 check_verify_result (
778 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
779 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
780 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
781 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
786 /* DCP with invalid CompositionMetadataAsset */
787 BOOST_AUTO_TEST_CASE (verify_test25)
789 boost::filesystem::path const dir("build/test/verify_test25");
790 prepare_directory (dir);
792 auto reel = make_shared<dcp::Reel>();
793 reel->add (black_picture_asset(dir));
794 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
796 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
797 cpl->set_main_sound_sample_rate (48000);
798 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
799 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
803 dcp.write_xml (dcp::SMPTE);
806 Editor e (find_cpl("build/test/verify_test25"));
807 e.replace ("meta:Width", "meta:WidthX");
810 check_verify_result (
812 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
817 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
818 BOOST_AUTO_TEST_CASE (verify_test26)
820 boost::filesystem::path const dir("build/test/verify_test26");
821 prepare_directory (dir);
822 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
823 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
824 asset->_language = "wrong-andbad";
825 asset->write (dir / "subs.mxf");
826 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
827 reel_asset->_language = "badlang";
828 write_dcp_with_single_asset (dir, reel_asset);
830 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
831 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
832 auto i = notes.begin();
833 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
834 BOOST_REQUIRE (i->note());
835 BOOST_CHECK_EQUAL (*i->note(), "badlang");
837 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
838 BOOST_REQUIRE (i->note());
839 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
841 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
845 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
846 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
848 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
849 prepare_directory (dir);
850 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
851 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
852 asset->_language = "wrong-andbad";
853 asset->write (dir / "subs.mxf");
854 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
855 reel_asset->_language = "badlang";
856 write_dcp_with_single_asset (dir, reel_asset);
858 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
859 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
860 auto i = notes.begin ();
861 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
862 BOOST_REQUIRE (i->note());
863 BOOST_CHECK_EQUAL (*i->note(), "badlang");
865 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
866 BOOST_REQUIRE (i->note());
867 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
869 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_CPL_METADATA);
873 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
874 * the release territory.
876 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
878 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
879 prepare_directory (dir);
881 auto picture = simple_picture (dir, "foo");
882 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
883 auto reel = make_shared<dcp::Reel>();
884 reel->add (reel_picture);
885 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
886 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
887 reel->add (reel_sound);
888 reel->add (simple_markers());
890 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
892 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
893 cpl->_additional_subtitle_languages.push_back("andso-is-this");
894 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
895 cpl->set_main_sound_sample_rate (48000);
896 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
897 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
898 cpl->set_version_number (1);
899 cpl->_release_territory = "fred-jim";
900 auto dcp = make_shared<dcp::DCP>(dir);
902 dcp->write_xml (dcp::SMPTE);
904 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
905 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
906 auto i = notes.begin ();
907 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
908 BOOST_REQUIRE (i->note());
909 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
911 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
912 BOOST_REQUIRE (i->note());
913 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
915 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
916 BOOST_REQUIRE (i->note());
917 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
919 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
920 BOOST_REQUIRE (i->note());
921 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
926 vector<dcp::VerificationNote>
927 check_picture_size (int width, int height, int frame_rate, bool three_d)
929 using namespace boost::filesystem;
931 path dcp_path = "build/test/verify_picture_test";
932 remove_all (dcp_path);
933 create_directories (dcp_path);
935 shared_ptr<dcp::PictureAsset> mp;
937 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
939 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
941 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
943 auto image = black_image (dcp::Size(width, height));
944 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
945 int const length = three_d ? frame_rate * 2 : frame_rate;
946 for (int i = 0; i < length; ++i) {
947 picture_writer->write (j2c.data(), j2c.size());
949 picture_writer->finalize ();
951 auto d = make_shared<dcp::DCP>(dcp_path);
952 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
953 cpl->set_annotation_text ("A Test DCP");
954 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
955 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
956 cpl->set_main_sound_sample_rate (48000);
957 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
958 cpl->set_main_picture_active_area (dcp::Size(1998, 1080));
959 cpl->set_version_number (1);
961 auto reel = make_shared<dcp::Reel>();
964 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
966 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
969 reel->add (simple_markers(frame_rate));
974 d->write_xml (dcp::SMPTE);
976 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
982 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
984 auto notes = check_picture_size(width, height, frame_rate, three_d);
986 BOOST_CHECK_EQUAL (notes.size(), 0U);
992 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
994 auto notes = check_picture_size(width, height, frame_rate, three_d);
995 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
996 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
997 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
1003 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1005 auto notes = check_picture_size(width, height, frame_rate, three_d);
1006 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1007 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1008 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
1014 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1016 auto notes = check_picture_size(width, height, frame_rate, three_d);
1017 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1018 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1019 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1023 BOOST_AUTO_TEST_CASE (verify_picture_size)
1025 using namespace boost::filesystem;
1028 check_picture_size_ok (2048, 858, 24, false);
1029 check_picture_size_ok (2048, 858, 25, false);
1030 check_picture_size_ok (2048, 858, 48, false);
1031 check_picture_size_ok (2048, 858, 24, true);
1032 check_picture_size_ok (2048, 858, 25, true);
1033 check_picture_size_ok (2048, 858, 48, true);
1036 check_picture_size_ok (1998, 1080, 24, false);
1037 check_picture_size_ok (1998, 1080, 25, false);
1038 check_picture_size_ok (1998, 1080, 48, false);
1039 check_picture_size_ok (1998, 1080, 24, true);
1040 check_picture_size_ok (1998, 1080, 25, true);
1041 check_picture_size_ok (1998, 1080, 48, true);
1044 check_picture_size_ok (4096, 1716, 24, false);
1047 check_picture_size_ok (3996, 2160, 24, false);
1049 /* Bad frame size */
1050 check_picture_size_bad_frame_size (2050, 858, 24, false);
1051 check_picture_size_bad_frame_size (2048, 658, 25, false);
1052 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1053 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1055 /* Bad 2K frame rate */
1056 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1057 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1058 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1060 /* Bad 4K frame rate */
1061 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1062 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1065 auto notes = check_picture_size(3996, 2160, 24, true);
1066 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1067 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1068 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1074 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1077 make_shared<dcp::SubtitleString>(
1085 dcp::Time(start_frame, 24, 24),
1086 dcp::Time(end_frame, 24, 24),
1102 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1104 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1105 prepare_directory (dir);
1107 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1108 for (int i = 0; i < 2048; ++i) {
1109 add_test_subtitle (asset, i * 24, i * 24 + 20);
1111 asset->set_language (dcp::LanguageTag("de-DE"));
1112 asset->write (dir / "subs.mxf");
1113 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1114 write_dcp_with_single_asset (dir, reel_asset);
1116 check_verify_result (
1119 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1120 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1121 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1122 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1128 shared_ptr<dcp::SMPTESubtitleAsset>
1129 make_large_subtitle_asset (boost::filesystem::path font_file)
1131 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1132 dcp::ArrayData big_fake_font(1024 * 1024);
1133 big_fake_font.write (font_file);
1134 for (int i = 0; i < 116; ++i) {
1135 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1143 verify_timed_text_asset_too_large (string name)
1145 auto const dir = boost::filesystem::path("build/test") / name;
1146 prepare_directory (dir);
1147 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1148 add_test_subtitle (asset, 0, 20);
1149 asset->set_language (dcp::LanguageTag("de-DE"));
1150 asset->write (dir / "subs.mxf");
1152 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1153 write_dcp_with_single_asset (dir, reel_asset);
1155 check_verify_result (
1158 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1159 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1160 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1161 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1162 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA },
1167 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1169 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1170 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1174 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1176 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1177 prepare_directory (dir);
1178 auto dcp = make_simple (dir, 1, 240);
1181 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1182 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1183 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1184 "<ContentTitleText>Content</ContentTitleText>"
1185 "<AnnotationText>Annotation</AnnotationText>"
1186 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1187 "<ReelNumber>1</ReelNumber>"
1188 "<EditRate>25 1</EditRate>"
1189 "<TimeCodeRate>25</TimeCodeRate>"
1190 "<StartTime>00:00:00:00</StartTime>"
1191 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1193 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1194 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1195 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1201 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1202 BOOST_REQUIRE (xml_file);
1203 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1205 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1206 subs->write (dir / "subs.mxf");
1208 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1209 dcp->cpls().front()->reels().front()->add(reel_subs);
1210 dcp->write_xml (dcp::SMPTE);
1212 check_verify_result (
1215 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1216 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1221 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1223 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1224 auto dcp = make_simple (path, 2, 240);
1225 auto cpl = dcp->cpls()[0];
1228 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1229 subs->set_language (dcp::LanguageTag("de-DE"));
1230 subs->add (simple_subtitle());
1231 subs->write (path / "subs1.mxf");
1232 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1233 cpl->reels()[0]->add(reel_subs);
1237 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1238 subs->set_language (dcp::LanguageTag("en-US"));
1239 subs->add (simple_subtitle());
1240 subs->write (path / "subs2.mxf");
1241 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1242 cpl->reels()[1]->add(reel_subs);
1245 dcp->write_xml (dcp::SMPTE);
1247 check_verify_result (
1250 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1251 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1252 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1257 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1259 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1260 prepare_directory (dir);
1261 auto dcp = make_simple (dir, 1, 240);
1264 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1265 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1266 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1267 "<ContentTitleText>Content</ContentTitleText>"
1268 "<AnnotationText>Annotation</AnnotationText>"
1269 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1270 "<ReelNumber>1</ReelNumber>"
1271 "<Language>de-DE</Language>"
1272 "<EditRate>25 1</EditRate>"
1273 "<TimeCodeRate>25</TimeCodeRate>"
1274 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1276 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1277 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1278 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1284 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1285 BOOST_REQUIRE (xml_file);
1286 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1288 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1289 subs->write (dir / "subs.mxf");
1291 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1292 dcp->cpls().front()->reels().front()->add(reel_subs);
1293 dcp->write_xml (dcp::SMPTE);
1295 check_verify_result (
1298 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1299 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1304 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1306 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1307 prepare_directory (dir);
1308 auto dcp = make_simple (dir, 1, 240);
1311 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1312 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1313 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1314 "<ContentTitleText>Content</ContentTitleText>"
1315 "<AnnotationText>Annotation</AnnotationText>"
1316 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1317 "<ReelNumber>1</ReelNumber>"
1318 "<Language>de-DE</Language>"
1319 "<EditRate>25 1</EditRate>"
1320 "<TimeCodeRate>25</TimeCodeRate>"
1321 "<StartTime>00:00:02:00</StartTime>"
1322 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1324 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1325 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1326 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1332 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1333 BOOST_REQUIRE (xml_file);
1334 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1336 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1337 subs->write (dir / "subs.mxf");
1339 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1340 dcp->cpls().front()->reels().front()->add(reel_subs);
1341 dcp->write_xml (dcp::SMPTE);
1343 check_verify_result (
1346 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1347 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1355 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1358 , v_position(v_position_)
1371 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1373 prepare_directory (dir);
1374 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1375 asset->set_start_time (dcp::Time());
1376 for (auto i: subs) {
1377 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1379 asset->set_language (dcp::LanguageTag("de-DE"));
1380 asset->write (dir / "subs.mxf");
1382 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1383 write_dcp_with_single_asset (dir, reel_asset);
1387 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1389 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1390 /* Just too early */
1391 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1392 check_verify_result (
1395 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY },
1396 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1402 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1404 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1405 /* Just late enough */
1406 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1407 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1411 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1413 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1414 prepare_directory (dir);
1416 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1417 asset1->set_start_time (dcp::Time());
1418 /* Just late enough */
1419 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1420 asset1->set_language (dcp::LanguageTag("de-DE"));
1421 asset1->write (dir / "subs1.mxf");
1422 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1423 auto reel1 = make_shared<dcp::Reel>();
1424 reel1->add (reel_asset1);
1425 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1426 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1427 reel1->add (markers1);
1429 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1430 asset2->set_start_time (dcp::Time());
1431 /* This would be too early on first reel but should be OK on the second */
1432 add_test_subtitle (asset2, 0, 4 * 24);
1433 asset2->set_language (dcp::LanguageTag("de-DE"));
1434 asset2->write (dir / "subs2.mxf");
1435 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1436 auto reel2 = make_shared<dcp::Reel>();
1437 reel2->add (reel_asset2);
1438 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1439 markers2->set (dcp::Marker::LFOC, dcp::Time(16 * 24 - 1, 24, 24));
1440 reel2->add (markers2);
1442 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1445 auto dcp = make_shared<dcp::DCP>(dir);
1447 dcp->write_xml (dcp::SMPTE);
1449 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1453 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1455 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1456 dcp_with_text<dcp::ReelSubtitleAsset> (
1460 { 5 * 24 + 1, 6 * 24 },
1462 check_verify_result (
1465 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE },
1466 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1471 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1473 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1474 dcp_with_text<dcp::ReelSubtitleAsset> (
1478 { 5 * 24 + 16, 8 * 24 },
1480 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1484 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1486 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1487 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1488 check_verify_result (
1491 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT },
1492 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1497 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1499 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1500 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1501 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1505 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1507 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1508 dcp_with_text<dcp::ReelSubtitleAsset> (
1511 { 96, 200, 0.0, "We" },
1512 { 96, 200, 0.1, "have" },
1513 { 96, 200, 0.2, "four" },
1514 { 96, 200, 0.3, "lines" }
1516 check_verify_result (
1519 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1520 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1525 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1527 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1528 dcp_with_text<dcp::ReelSubtitleAsset> (
1531 { 96, 200, 0.0, "We" },
1532 { 96, 200, 0.1, "have" },
1533 { 96, 200, 0.2, "four" },
1535 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1539 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1541 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1542 dcp_with_text<dcp::ReelSubtitleAsset> (
1545 { 96, 300, 0.0, "We" },
1546 { 96, 300, 0.1, "have" },
1547 { 150, 180, 0.2, "four" },
1548 { 150, 180, 0.3, "lines" }
1550 check_verify_result (
1553 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES },
1554 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1559 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1561 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1562 dcp_with_text<dcp::ReelSubtitleAsset> (
1565 { 96, 300, 0.0, "We" },
1566 { 96, 300, 0.1, "have" },
1567 { 150, 180, 0.2, "four" },
1568 { 190, 250, 0.3, "lines" }
1570 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1574 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1576 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1577 dcp_with_text<dcp::ReelSubtitleAsset> (
1580 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1582 check_verify_result (
1585 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED },
1586 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1591 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1593 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1594 dcp_with_text<dcp::ReelSubtitleAsset> (
1597 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1599 check_verify_result (
1602 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG },
1603 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1608 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1610 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1611 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1614 { 96, 200, 0.0, "We" },
1615 { 96, 200, 0.1, "have" },
1616 { 96, 200, 0.2, "four" },
1617 { 96, 200, 0.3, "lines" }
1619 check_verify_result (
1622 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1623 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1628 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1630 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1631 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1634 { 96, 200, 0.0, "We" },
1635 { 96, 200, 0.1, "have" },
1636 { 96, 200, 0.2, "four" },
1638 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1642 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1644 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1645 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1648 { 96, 300, 0.0, "We" },
1649 { 96, 300, 0.1, "have" },
1650 { 150, 180, 0.2, "four" },
1651 { 150, 180, 0.3, "lines" }
1653 check_verify_result (
1656 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES},
1657 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1662 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1664 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1665 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1668 { 96, 300, 0.0, "We" },
1669 { 96, 300, 0.1, "have" },
1670 { 150, 180, 0.2, "four" },
1671 { 190, 250, 0.3, "lines" }
1673 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1677 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1679 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1680 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1683 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1685 check_verify_result (
1688 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG },
1689 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1694 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1696 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1697 prepare_directory (dir);
1699 auto picture = simple_picture (dir, "foo");
1700 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1701 auto reel = make_shared<dcp::Reel>();
1702 reel->add (reel_picture);
1703 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1704 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1705 reel->add (reel_sound);
1706 reel->add (simple_markers());
1707 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1709 auto dcp = make_shared<dcp::DCP>(dir);
1711 dcp->write_xml (dcp::SMPTE);
1713 check_verify_result (
1716 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE },
1717 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1722 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1724 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1725 auto dcp = make_simple (dir);
1726 dcp->write_xml (dcp::SMPTE);
1727 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1730 BOOST_REQUIRE (dcp->cpls()[0]->file());
1731 Editor e(dcp->cpls()[0]->file().get());
1732 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1735 check_verify_result (
1738 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1739 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1744 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1746 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1747 auto dcp = make_simple (dir);
1748 dcp->write_xml (dcp::SMPTE);
1749 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1752 BOOST_REQUIRE (dcp->cpls()[0]->file());
1753 Editor e(dcp->cpls()[0]->file().get());
1754 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1757 check_verify_result (
1760 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1761 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1766 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1768 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1769 boost::filesystem::remove_all (dir);
1770 boost::filesystem::create_directories (dir);
1771 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1772 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1774 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1775 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1777 auto reel = make_shared<dcp::Reel>(
1778 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1779 make_shared<dcp::ReelSoundAsset>(ms, 0)
1782 reel->add (simple_markers());
1786 dcp->write_xml (dcp::SMPTE);
1788 check_verify_result (
1791 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION },
1792 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1800 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1802 boost::filesystem::remove_all (dir);
1803 boost::filesystem::create_directories (dir);
1804 auto dcp = make_shared<dcp::DCP>(dir);
1805 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1807 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1808 subs->set_language (dcp::LanguageTag("de-DE"));
1809 subs->set_start_time (dcp::Time());
1810 subs->add (simple_subtitle());
1811 subs->write (dir / "subs.mxf");
1812 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1814 auto reel1 = make_shared<dcp::Reel>(
1815 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1816 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1820 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1823 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1824 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1825 reel1->add (markers1);
1829 auto reel2 = make_shared<dcp::Reel>(
1830 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1831 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1835 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1838 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1839 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1840 reel2->add (markers2);
1845 dcp->write_xml (dcp::SMPTE);
1849 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1852 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1853 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1854 check_verify_result (
1857 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS },
1858 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1864 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1865 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1866 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1870 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1871 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1872 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1879 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1881 boost::filesystem::remove_all (dir);
1882 boost::filesystem::create_directories (dir);
1883 auto dcp = make_shared<dcp::DCP>(dir);
1884 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1886 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1887 subs->set_language (dcp::LanguageTag("de-DE"));
1888 subs->set_start_time (dcp::Time());
1889 subs->add (simple_subtitle());
1890 subs->write (dir / "subs.mxf");
1892 auto reel1 = make_shared<dcp::Reel>(
1893 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1894 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1897 for (int i = 0; i < caps_in_reel1; ++i) {
1898 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1901 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1902 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1903 reel1->add (markers1);
1907 auto reel2 = make_shared<dcp::Reel>(
1908 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1909 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1912 for (int i = 0; i < caps_in_reel2; ++i) {
1913 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1916 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1917 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1918 reel2->add (markers2);
1923 dcp->write_xml (dcp::SMPTE);
1928 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
1931 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
1932 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
1933 check_verify_result (
1936 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER },
1937 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1942 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
1943 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
1944 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1948 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
1949 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
1950 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }});
1957 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
1959 boost::filesystem::remove_all (dir);
1960 boost::filesystem::create_directories (dir);
1961 auto dcp = make_shared<dcp::DCP>(dir);
1962 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1964 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1965 subs->set_language (dcp::LanguageTag("de-DE"));
1966 subs->set_start_time (dcp::Time());
1967 subs->add (simple_subtitle());
1968 subs->write (dir / "subs.mxf");
1969 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
1972 auto reel = make_shared<dcp::Reel>(
1973 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1974 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1977 reel->add (reel_text);
1979 reel->add (simple_markers(240));
1984 dcp->write_xml (dcp::SMPTE);
1986 check_verify_result (
1989 { dcp::VerificationNote::VERIFY_BV21_ERROR, code },
1990 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA }
1995 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
1997 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1998 "build/test/verify_subtitle_entry_point_must_be_present",
1999 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
2000 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2001 asset->unset_entry_point ();
2005 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
2006 "build/test/verify_subtitle_entry_point_must_be_zero",
2007 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
2008 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
2009 asset->set_entry_point (4);
2013 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2014 "build/test/verify_closed_caption_entry_point_must_be_present",
2015 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
2016 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2017 asset->unset_entry_point ();
2021 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
2022 "build/test/verify_closed_caption_entry_point_must_be_zero",
2023 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
2024 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
2025 asset->set_entry_point (9);
2031 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
2035 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
2036 auto dcp = make_simple (dir);
2037 dcp->write_xml (dcp::SMPTE);
2038 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
2041 BOOST_REQUIRE (dcp->cpls()[0]->file());
2042 Editor e(dcp->cpls()[0]->file().get());
2043 e.replace("<Hash>XGhFVrqZqapOJx5Fh2SLjj48Yjg=</Hash>", "");
2046 check_verify_result (
2049 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
2050 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
2057 verify_markers_test (
2058 boost::filesystem::path dir,
2059 vector<pair<dcp::Marker, dcp::Time>> markers,
2060 vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes
2063 auto dcp = make_simple (dir);
2064 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
2065 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24, 0);
2066 for (auto const& i: markers) {
2067 markers_asset->set (i.first, i.second);
2069 dcp->cpls()[0]->reels()[0]->add(markers_asset);
2070 dcp->write_xml (dcp::SMPTE);
2071 check_verify_result ({dir}, types_and_codes);
2075 BOOST_AUTO_TEST_CASE (verify_markers)
2077 verify_markers_test (
2078 "build/test/verify_markers_all_correct",
2080 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2081 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2082 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2083 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2088 verify_markers_test (
2089 "build/test/verify_markers_missing_ffec",
2091 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2092 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2093 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2096 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2099 verify_markers_test (
2100 "build/test/verify_markers_missing_ffmc",
2102 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2103 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2104 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2107 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2110 verify_markers_test (
2111 "build/test/verify_markers_missing_ffoc",
2113 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2114 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2115 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2118 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2121 verify_markers_test (
2122 "build/test/verify_markers_missing_lfoc",
2124 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2125 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2126 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2129 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2132 verify_markers_test (
2133 "build/test/verify_markers_incorrect_ffoc",
2135 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2136 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2137 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2138 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2141 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2144 verify_markers_test (
2145 "build/test/verify_markers_incorrect_lfoc",
2147 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2148 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2149 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2150 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2153 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }
2158 BOOST_AUTO_TEST_CASE (verify_cpl_metadata_version)
2160 boost::filesystem::path dir = "build/test/verify_cpl_metadata_version";
2161 prepare_directory (dir);
2162 auto dcp = make_simple (dir);
2163 dcp->cpls()[0]->unset_version_number();
2164 dcp->write_xml (dcp::SMPTE);
2165 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_CPL_METADATA_VERSION_NUMBER }});