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 "compose.hpp"
53 #include <boost/test/unit_test.hpp>
54 #include <boost/foreach.hpp>
55 #include <boost/algorithm/string.hpp>
64 using std::make_shared;
65 using boost::optional;
66 using std::shared_ptr;
69 static list<pair<string, optional<boost::filesystem::path> > > stages;
72 stage (string s, optional<boost::filesystem::path> p)
74 stages.push_back (make_pair (s, p));
84 prepare_directory (boost::filesystem::path path)
86 using namespace boost::filesystem;
88 create_directories (path);
92 static vector<boost::filesystem::path>
93 setup (int reference_number, int verify_test_number)
95 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
96 for (auto i: boost::filesystem::directory_iterator(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number))) {
97 boost::filesystem::copy_file (i.path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i.path().filename());
100 return { dcp::String::compose("build/test/verify_test%1", verify_test_number) };
107 write_dcp_with_single_asset (boost::filesystem::path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::SMPTE)
109 auto reel = make_shared<dcp::Reel>();
110 reel->add (reel_asset);
111 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
113 auto dcp = make_shared<dcp::DCP>(dir);
115 dcp->write_xml (standard);
119 /** Class that can alter a file by searching and replacing strings within it.
120 * On destruction modifies the file whose name was given to the constructor.
125 Editor (boost::filesystem::path path)
128 _content = dcp::file_to_string (_path);
133 auto f = fopen(_path.string().c_str(), "w");
135 fwrite (_content.c_str(), _content.length(), 1, f);
139 void replace (string a, string b)
141 auto old_content = _content;
142 boost::algorithm::replace_all (_content, a, b);
143 BOOST_REQUIRE (_content != old_content);
147 boost::filesystem::path _path;
148 std::string _content;
154 dump_notes (vector<dcp::VerificationNote> const & notes)
156 for (auto i: notes) {
157 std::cout << dcp::note_to_string(i) << "\n";
164 check_verify_result (vector<boost::filesystem::path> dir, vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes)
166 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
168 BOOST_REQUIRE_EQUAL (notes.size(), types_and_codes.size());
169 auto i = notes.begin();
170 auto j = types_and_codes.begin();
171 while (i != notes.end()) {
172 BOOST_CHECK_EQUAL (i->type(), j->first);
173 BOOST_CHECK_EQUAL (i->code(), j->second);
182 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
184 auto directories = setup (1, n);
188 e.replace (from, to);
191 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
193 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
194 auto i = notes.begin();
195 auto j = codes.begin();
196 while (i != notes.end()) {
197 BOOST_CHECK_EQUAL (i->code(), *j);
204 /* Check DCP as-is (should be OK) */
205 BOOST_AUTO_TEST_CASE (verify_test1)
208 auto directories = setup (1, 1);
209 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
211 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
212 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
213 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
215 auto st = stages.begin();
216 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
217 BOOST_REQUIRE (st->second);
218 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
220 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
221 BOOST_REQUIRE (st->second);
222 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
224 BOOST_CHECK_EQUAL (st->first, "Checking reel");
225 BOOST_REQUIRE (!st->second);
227 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
228 BOOST_REQUIRE (st->second);
229 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
231 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
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 sound asset hash");
236 BOOST_REQUIRE (st->second);
237 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
239 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
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 PKL");
244 BOOST_REQUIRE (st->second);
245 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
247 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
248 BOOST_REQUIRE (st->second);
249 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
251 BOOST_REQUIRE (st == stages.end());
253 BOOST_CHECK_EQUAL (notes.size(), 0);
256 /* Corrupt the MXFs and check that this is spotted */
257 BOOST_AUTO_TEST_CASE (verify_test2)
259 auto directories = setup (1, 2);
261 auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
263 fseek (mod, 4096, SEEK_SET);
265 fwrite (&x, sizeof(x), 1, mod);
268 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
270 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
271 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
274 dcp::ASDCPErrorSuspender sus;
275 check_verify_result (
278 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
279 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
283 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
284 BOOST_AUTO_TEST_CASE (verify_test3)
286 auto directories = setup (1, 3);
289 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
290 e.replace ("<Hash>", "<Hash>x");
293 check_verify_result (
296 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
297 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER },
298 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER },
299 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
300 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
301 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
305 /* Corrupt the ContentKind in the CPL */
306 BOOST_AUTO_TEST_CASE (verify_test4)
308 auto directories = setup (1, 4);
311 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
312 e.replace ("<ContentKind>", "<ContentKind>x");
315 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
317 BOOST_REQUIRE_EQUAL (notes.size(), 1);
318 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
319 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xtrailer'");
323 boost::filesystem::path
326 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
330 boost::filesystem::path
333 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
337 boost::filesystem::path
340 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
345 BOOST_AUTO_TEST_CASE (verify_test5)
347 check_verify_result_after_replace (
349 "<FrameRate>24 1", "<FrameRate>99 1",
350 { dcp::VerificationNote::CPL_HASH_INCORRECT,
351 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
356 BOOST_AUTO_TEST_CASE (verify_test6)
358 auto directories = setup (1, 6);
360 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
361 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
365 boost::filesystem::path
368 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
371 /* Empty asset filename in ASSETMAP */
372 BOOST_AUTO_TEST_CASE (verify_test7)
374 check_verify_result_after_replace (
376 "<Path>video.mxf</Path>", "<Path></Path>",
377 { dcp::VerificationNote::EMPTY_ASSET_PATH }
381 /* Mismatched standard */
382 BOOST_AUTO_TEST_CASE (verify_test8)
384 check_verify_result_after_replace (
386 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
387 { dcp::VerificationNote::MISMATCHED_STANDARD,
388 dcp::VerificationNote::XML_VALIDATION_ERROR,
389 dcp::VerificationNote::CPL_HASH_INCORRECT }
393 /* Badly formatted <Id> in CPL */
394 BOOST_AUTO_TEST_CASE (verify_test9)
396 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
397 check_verify_result_after_replace (
399 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
400 { dcp::VerificationNote::XML_VALIDATION_ERROR }
404 /* Badly formatted <IssueDate> in CPL */
405 BOOST_AUTO_TEST_CASE (verify_test10)
407 check_verify_result_after_replace (
409 "<IssueDate>", "<IssueDate>x",
410 { dcp::VerificationNote::XML_VALIDATION_ERROR,
411 dcp::VerificationNote::CPL_HASH_INCORRECT }
415 /* Badly-formatted <Id> in PKL */
416 BOOST_AUTO_TEST_CASE (verify_test11)
418 check_verify_result_after_replace (
420 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
421 { dcp::VerificationNote::XML_VALIDATION_ERROR }
425 /* Badly-formatted <Id> in ASSETMAP */
426 BOOST_AUTO_TEST_CASE (verify_test12)
428 check_verify_result_after_replace (
430 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
431 { dcp::VerificationNote::XML_VALIDATION_ERROR }
435 /* Basic test of an Interop DCP */
436 BOOST_AUTO_TEST_CASE (verify_test13)
439 auto directories = setup (3, 13);
440 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
442 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
443 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
444 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
446 auto st = stages.begin();
447 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
448 BOOST_REQUIRE (st->second);
449 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
451 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
452 BOOST_REQUIRE (st->second);
453 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
455 BOOST_CHECK_EQUAL (st->first, "Checking reel");
456 BOOST_REQUIRE (!st->second);
458 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
459 BOOST_REQUIRE (st->second);
460 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
462 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
463 BOOST_REQUIRE (st->second);
464 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
466 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
467 BOOST_REQUIRE (st->second);
468 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
470 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
471 BOOST_REQUIRE (st->second);
472 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
474 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
475 BOOST_REQUIRE (st->second);
476 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
478 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
479 BOOST_REQUIRE (st->second);
480 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
482 BOOST_REQUIRE (st == stages.end());
484 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
485 auto i = notes.begin ();
486 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
487 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
490 /* DCP with a short asset */
491 BOOST_AUTO_TEST_CASE (verify_test14)
493 auto directories = setup (8, 14);
494 check_verify_result (
497 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
498 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
499 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
500 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
501 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
508 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
510 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
511 boost::filesystem::create_directories (dir);
512 auto writer = asset->start_write (dir / "pic.mxf", true);
513 for (int i = 0; i < 24; ++i) {
514 writer->write (frame.data(), frame.size());
518 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
519 write_dcp_with_single_asset (dir, reel_asset);
523 /* DCP with an over-sized JPEG2000 frame */
524 BOOST_AUTO_TEST_CASE (verify_test15)
526 int const too_big = 1302083 * 2;
528 /* Compress a black image */
529 auto image = black_image ();
530 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
531 BOOST_REQUIRE (frame.size() < too_big);
533 /* Place it in a bigger block with some zero padding at the end */
534 dcp::ArrayData oversized_frame(too_big);
535 memcpy (oversized_frame.data(), frame.data(), frame.size());
536 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
538 boost::filesystem::path const dir("build/test/verify_test15");
539 boost::filesystem::remove_all (dir);
540 dcp_from_frame (oversized_frame, dir);
542 check_verify_result (
544 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES }}
549 /* DCP with a nearly over-sized JPEG2000 frame */
550 BOOST_AUTO_TEST_CASE (verify_test16)
552 int const nearly_too_big = 1302083 * 0.98;
554 /* Compress a black image */
555 auto image = black_image ();
556 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
557 BOOST_REQUIRE (frame.size() < nearly_too_big);
559 /* Place it in a bigger block with some zero padding at the end */
560 dcp::ArrayData oversized_frame(nearly_too_big);
561 memcpy (oversized_frame.data(), frame.data(), frame.size());
562 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
564 boost::filesystem::path const dir("build/test/verify_test16");
565 boost::filesystem::remove_all (dir);
566 dcp_from_frame (oversized_frame, dir);
568 check_verify_result (
570 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES }}
575 /* DCP with a within-range JPEG2000 frame */
576 BOOST_AUTO_TEST_CASE (verify_test17)
578 /* Compress a black image */
579 auto image = black_image ();
580 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
581 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
583 boost::filesystem::path const dir("build/test/verify_test17");
584 boost::filesystem::remove_all (dir);
585 dcp_from_frame (frame, dir);
587 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
588 BOOST_REQUIRE_EQUAL (notes.size(), 0);
592 /* DCP with valid Interop subtitles */
593 BOOST_AUTO_TEST_CASE (verify_test18)
595 boost::filesystem::path const dir("build/test/verify_test18");
596 prepare_directory (dir);
597 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
598 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
599 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
600 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
602 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
606 /* DCP with broken Interop subtitles */
607 BOOST_AUTO_TEST_CASE (verify_test19)
609 boost::filesystem::path const dir("build/test/verify_test19");
610 prepare_directory (dir);
611 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
612 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
613 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
614 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
617 Editor e (dir / "subs.xml");
618 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
621 check_verify_result (
624 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
625 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
626 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
631 /* DCP with valid SMPTE subtitles */
632 BOOST_AUTO_TEST_CASE (verify_test20)
634 boost::filesystem::path const dir("build/test/verify_test20");
635 prepare_directory (dir);
636 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
637 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
638 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
639 write_dcp_with_single_asset (dir, reel_asset);
641 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
642 BOOST_REQUIRE_EQUAL (notes.size(), 0);
646 /* DCP with broken SMPTE subtitles */
647 BOOST_AUTO_TEST_CASE (verify_test21)
649 boost::filesystem::path const dir("build/test/verify_test21");
650 prepare_directory (dir);
651 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
652 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
653 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
654 write_dcp_with_single_asset (dir, reel_asset);
656 check_verify_result (
659 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
660 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
661 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
667 BOOST_AUTO_TEST_CASE (verify_test22)
669 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
670 prepare_directory (ov_dir);
672 auto image = black_image ();
673 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
674 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
675 dcp_from_frame (frame, ov_dir);
677 dcp::DCP ov (ov_dir);
680 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
681 prepare_directory (vf_dir);
683 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
685 check_verify_result (
687 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET }});
691 /* DCP with valid CompositionMetadataAsset */
692 BOOST_AUTO_TEST_CASE (verify_test23)
694 boost::filesystem::path const dir("build/test/verify_test23");
695 prepare_directory (dir);
697 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
698 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
699 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
701 auto reel = make_shared<dcp::Reel>();
702 reel->add (reel_asset);
703 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
705 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
706 cpl->set_main_sound_sample_rate (48000);
707 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
708 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
712 dcp.write_xml (dcp::SMPTE);
714 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
715 BOOST_CHECK (notes.empty());
719 boost::filesystem::path find_cpl (boost::filesystem::path dir)
721 for (auto i: boost::filesystem::directory_iterator(dir)) {
722 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
727 BOOST_REQUIRE (false);
732 /* DCP with invalid CompositionMetadataAsset */
733 BOOST_AUTO_TEST_CASE (verify_test24)
735 boost::filesystem::path const dir("build/test/verify_test24");
736 prepare_directory (dir);
738 auto reel = make_shared<dcp::Reel>();
739 reel->add (black_picture_asset(dir));
740 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
742 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
743 cpl->set_main_sound_sample_rate (48000);
744 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
745 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
749 dcp.write_xml (dcp::SMPTE);
752 Editor e (find_cpl("build/test/verify_test24"));
753 e.replace ("MainSound", "MainSoundX");
756 check_verify_result (
759 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
760 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
761 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
762 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
767 /* DCP with invalid CompositionMetadataAsset */
768 BOOST_AUTO_TEST_CASE (verify_test25)
770 boost::filesystem::path const dir("build/test/verify_test25");
771 prepare_directory (dir);
773 auto reel = make_shared<dcp::Reel>();
774 reel->add (black_picture_asset(dir));
775 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
777 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
778 cpl->set_main_sound_sample_rate (48000);
779 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
780 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
784 dcp.write_xml (dcp::SMPTE);
787 Editor e (find_cpl("build/test/verify_test25"));
788 e.replace ("meta:Width", "meta:WidthX");
791 check_verify_result (
793 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
798 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
799 BOOST_AUTO_TEST_CASE (verify_test26)
801 boost::filesystem::path const dir("build/test/verify_test26");
802 prepare_directory (dir);
803 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
804 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
805 asset->_language = "wrong-andbad";
806 asset->write (dir / "subs.mxf");
807 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
808 reel_asset->_language = "badlang";
809 write_dcp_with_single_asset (dir, reel_asset);
811 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
812 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
813 auto i = notes.begin();
814 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
815 BOOST_REQUIRE (i->note());
816 BOOST_CHECK_EQUAL (*i->note(), "badlang");
818 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
819 BOOST_REQUIRE (i->note());
820 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
824 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
825 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
827 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
828 prepare_directory (dir);
829 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
830 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
831 asset->_language = "wrong-andbad";
832 asset->write (dir / "subs.mxf");
833 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
834 reel_asset->_language = "badlang";
835 write_dcp_with_single_asset (dir, reel_asset);
837 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
838 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
839 auto i = notes.begin ();
840 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
841 BOOST_REQUIRE (i->note());
842 BOOST_CHECK_EQUAL (*i->note(), "badlang");
844 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
845 BOOST_REQUIRE (i->note());
846 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
850 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
851 * the release territory.
853 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
855 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
856 prepare_directory (dir);
858 auto picture = simple_picture (dir, "foo");
859 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
860 auto reel = make_shared<dcp::Reel>();
861 reel->add (reel_picture);
862 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
863 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
864 reel->add (reel_sound);
865 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
867 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
868 cpl->_additional_subtitle_languages.push_back("andso-is-this");
869 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
870 cpl->set_main_sound_sample_rate (48000);
871 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
872 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
873 cpl->_release_territory = "fred-jim";
874 auto dcp = make_shared<dcp::DCP>(dir);
876 dcp->write_xml (dcp::SMPTE);
878 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
879 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
880 auto i = notes.begin ();
881 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
882 BOOST_REQUIRE (i->note());
883 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
885 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
886 BOOST_REQUIRE (i->note());
887 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
889 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
890 BOOST_REQUIRE (i->note());
891 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
893 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
894 BOOST_REQUIRE (i->note());
895 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
901 vector<dcp::VerificationNote>
902 check_picture_size (int width, int height, int frame_rate, bool three_d)
904 using namespace boost::filesystem;
906 path dcp_path = "build/test/verify_picture_test";
907 remove_all (dcp_path);
908 create_directories (dcp_path);
910 shared_ptr<dcp::PictureAsset> mp;
912 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
914 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
916 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
918 auto image = black_image (dcp::Size(width, height));
919 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
920 int const length = three_d ? frame_rate * 2 : frame_rate;
921 for (int i = 0; i < length; ++i) {
922 picture_writer->write (j2c.data(), j2c.size());
924 picture_writer->finalize ();
926 auto d = make_shared<dcp::DCP>(dcp_path);
927 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
928 cpl->set_annotation_text ("A Test DCP");
929 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
931 auto reel = make_shared<dcp::Reel>();
934 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
936 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
942 d->write_xml (dcp::SMPTE);
944 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
950 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
952 auto notes = check_picture_size(width, height, frame_rate, three_d);
954 BOOST_CHECK_EQUAL (notes.size(), 0U);
960 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
962 auto notes = check_picture_size(width, height, frame_rate, three_d);
963 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
964 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
965 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
971 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
973 auto notes = check_picture_size(width, height, frame_rate, three_d);
974 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
975 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
976 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
982 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
984 auto notes = check_picture_size(width, height, frame_rate, three_d);
985 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
986 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
987 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
991 BOOST_AUTO_TEST_CASE (verify_picture_size)
993 using namespace boost::filesystem;
996 check_picture_size_ok (2048, 858, 24, false);
997 check_picture_size_ok (2048, 858, 25, false);
998 check_picture_size_ok (2048, 858, 48, false);
999 check_picture_size_ok (2048, 858, 24, true);
1000 check_picture_size_ok (2048, 858, 25, true);
1001 check_picture_size_ok (2048, 858, 48, true);
1004 check_picture_size_ok (1998, 1080, 24, false);
1005 check_picture_size_ok (1998, 1080, 25, false);
1006 check_picture_size_ok (1998, 1080, 48, false);
1007 check_picture_size_ok (1998, 1080, 24, true);
1008 check_picture_size_ok (1998, 1080, 25, true);
1009 check_picture_size_ok (1998, 1080, 48, true);
1012 check_picture_size_ok (4096, 1716, 24, false);
1015 check_picture_size_ok (3996, 2160, 24, false);
1017 /* Bad frame size */
1018 check_picture_size_bad_frame_size (2050, 858, 24, false);
1019 check_picture_size_bad_frame_size (2048, 658, 25, false);
1020 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1021 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1023 /* Bad 2K frame rate */
1024 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1025 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1026 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1028 /* Bad 4K frame rate */
1029 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1030 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1033 auto notes = check_picture_size(3996, 2160, 24, true);
1034 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1035 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1036 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1042 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1045 make_shared<dcp::SubtitleString>(
1053 dcp::Time(start_frame, 24, 24),
1054 dcp::Time(end_frame, 24, 24),
1070 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1072 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1073 prepare_directory (dir);
1075 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1076 for (int i = 0; i < 2048; ++i) {
1077 add_test_subtitle (asset, i * 24, i * 24 + 20);
1079 asset->set_language (dcp::LanguageTag("de-DE"));
1080 asset->write (dir / "subs.mxf");
1081 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1082 write_dcp_with_single_asset (dir, reel_asset);
1084 check_verify_result (
1087 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1088 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1089 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1095 shared_ptr<dcp::SMPTESubtitleAsset>
1096 make_large_subtitle_asset (boost::filesystem::path font_file)
1098 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1099 dcp::ArrayData big_fake_font(1024 * 1024);
1100 big_fake_font.write (font_file);
1101 for (int i = 0; i < 116; ++i) {
1102 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1110 verify_timed_text_asset_too_large (string name)
1112 auto const dir = boost::filesystem::path("build/test") / name;
1113 prepare_directory (dir);
1114 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1115 add_test_subtitle (asset, 0, 20);
1116 asset->set_language (dcp::LanguageTag("de-DE"));
1117 asset->write (dir / "subs.mxf");
1119 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1120 write_dcp_with_single_asset (dir, reel_asset);
1122 check_verify_result (
1125 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1126 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1127 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1128 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1133 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1135 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1136 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1140 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1142 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1143 prepare_directory (dir);
1144 auto dcp = make_simple (dir, 1, 240);
1147 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1148 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1149 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1150 "<ContentTitleText>Content</ContentTitleText>"
1151 "<AnnotationText>Annotation</AnnotationText>"
1152 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1153 "<ReelNumber>1</ReelNumber>"
1154 "<EditRate>25 1</EditRate>"
1155 "<TimeCodeRate>25</TimeCodeRate>"
1156 "<StartTime>00:00:00:00</StartTime>"
1157 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1159 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1160 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1161 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1167 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1168 BOOST_REQUIRE (xml_file);
1169 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1171 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1172 subs->write (dir / "subs.mxf");
1174 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1175 dcp->cpls().front()->reels().front()->add(reel_subs);
1176 dcp->write_xml (dcp::SMPTE);
1178 check_verify_result (
1181 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1182 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1187 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1189 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1190 auto dcp = make_simple (path, 2, 240);
1191 auto cpl = dcp->cpls()[0];
1194 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1195 subs->set_language (dcp::LanguageTag("de-DE"));
1196 subs->add (simple_subtitle());
1197 subs->write (path / "subs1.mxf");
1198 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1199 cpl->reels()[0]->add(reel_subs);
1203 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1204 subs->set_language (dcp::LanguageTag("en-US"));
1205 subs->add (simple_subtitle());
1206 subs->write (path / "subs2.mxf");
1207 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1208 cpl->reels()[1]->add(reel_subs);
1211 dcp->write_xml (dcp::SMPTE);
1213 check_verify_result (
1216 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1217 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1218 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1223 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1225 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1226 prepare_directory (dir);
1227 auto dcp = make_simple (dir, 1, 240);
1230 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1231 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1232 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1233 "<ContentTitleText>Content</ContentTitleText>"
1234 "<AnnotationText>Annotation</AnnotationText>"
1235 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1236 "<ReelNumber>1</ReelNumber>"
1237 "<Language>de-DE</Language>"
1238 "<EditRate>25 1</EditRate>"
1239 "<TimeCodeRate>25</TimeCodeRate>"
1240 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1242 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1243 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1244 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1250 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1251 BOOST_REQUIRE (xml_file);
1252 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1254 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1255 subs->write (dir / "subs.mxf");
1257 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1258 dcp->cpls().front()->reels().front()->add(reel_subs);
1259 dcp->write_xml (dcp::SMPTE);
1261 check_verify_result (
1264 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1265 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1270 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1272 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1273 prepare_directory (dir);
1274 auto dcp = make_simple (dir, 1, 240);
1277 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1278 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1279 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1280 "<ContentTitleText>Content</ContentTitleText>"
1281 "<AnnotationText>Annotation</AnnotationText>"
1282 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1283 "<ReelNumber>1</ReelNumber>"
1284 "<Language>de-DE</Language>"
1285 "<EditRate>25 1</EditRate>"
1286 "<TimeCodeRate>25</TimeCodeRate>"
1287 "<StartTime>00:00:02:00</StartTime>"
1288 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1290 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1291 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1292 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1298 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1299 BOOST_REQUIRE (xml_file);
1300 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1302 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1303 subs->write (dir / "subs.mxf");
1305 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1306 dcp->cpls().front()->reels().front()->add(reel_subs);
1307 dcp->write_xml (dcp::SMPTE);
1309 check_verify_result (
1312 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1313 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1321 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1324 , v_position(v_position_)
1337 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1339 prepare_directory (dir);
1340 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1341 asset->set_start_time (dcp::Time());
1342 for (auto i: subs) {
1343 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1345 asset->set_language (dcp::LanguageTag("de-DE"));
1346 asset->write (dir / "subs.mxf");
1348 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1349 write_dcp_with_single_asset (dir, reel_asset);
1353 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1355 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1356 /* Just too early */
1357 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1358 check_verify_result (
1360 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }});
1364 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1366 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1367 /* Just late enough */
1368 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1369 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1370 BOOST_REQUIRE (notes.empty());
1374 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1376 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1377 prepare_directory (dir);
1379 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1380 asset1->set_start_time (dcp::Time());
1381 /* Just late enough */
1382 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1383 asset1->set_language (dcp::LanguageTag("de-DE"));
1384 asset1->write (dir / "subs1.mxf");
1385 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1386 auto reel1 = make_shared<dcp::Reel>();
1387 reel1->add (reel_asset1);
1389 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1390 asset2->set_start_time (dcp::Time());
1391 /* This would be too early on first reel but should be OK on the second */
1392 add_test_subtitle (asset2, 0, 4 * 24);
1393 asset2->set_language (dcp::LanguageTag("de-DE"));
1394 asset2->write (dir / "subs2.mxf");
1395 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1396 auto reel2 = make_shared<dcp::Reel>();
1397 reel2->add (reel_asset2);
1399 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1402 auto dcp = make_shared<dcp::DCP>(dir);
1404 dcp->write_xml (dcp::SMPTE);
1406 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1407 BOOST_REQUIRE (notes.empty());
1411 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1413 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1414 dcp_with_text<dcp::ReelSubtitleAsset> (
1418 { 5 * 24 + 1, 6 * 24 },
1420 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE }});
1424 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1426 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1427 dcp_with_text<dcp::ReelSubtitleAsset> (
1431 { 5 * 24 + 16, 8 * 24 },
1433 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1434 BOOST_REQUIRE (notes.empty());
1438 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1440 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1441 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1442 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT }});
1446 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1448 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1449 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1450 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1451 BOOST_REQUIRE (notes.empty());
1455 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1457 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1458 dcp_with_text<dcp::ReelSubtitleAsset> (
1461 { 96, 200, 0.0, "We" },
1462 { 96, 200, 0.1, "have" },
1463 { 96, 200, 0.2, "four" },
1464 { 96, 200, 0.3, "lines" }
1466 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES}});
1470 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1472 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1473 dcp_with_text<dcp::ReelSubtitleAsset> (
1476 { 96, 200, 0.0, "We" },
1477 { 96, 200, 0.1, "have" },
1478 { 96, 200, 0.2, "four" },
1480 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1481 BOOST_REQUIRE (notes.empty());
1485 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1487 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1488 dcp_with_text<dcp::ReelSubtitleAsset> (
1491 { 96, 300, 0.0, "We" },
1492 { 96, 300, 0.1, "have" },
1493 { 150, 180, 0.2, "four" },
1494 { 150, 180, 0.3, "lines" }
1496 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES}});
1500 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1502 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1503 dcp_with_text<dcp::ReelSubtitleAsset> (
1506 { 96, 300, 0.0, "We" },
1507 { 96, 300, 0.1, "have" },
1508 { 150, 180, 0.2, "four" },
1509 { 190, 250, 0.3, "lines" }
1511 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1512 BOOST_REQUIRE (notes.empty());
1516 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1518 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1519 dcp_with_text<dcp::ReelSubtitleAsset> (
1522 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1524 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED }});
1528 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1530 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1531 dcp_with_text<dcp::ReelSubtitleAsset> (
1534 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1536 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG }});
1540 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1542 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1543 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1546 { 96, 200, 0.0, "We" },
1547 { 96, 200, 0.1, "have" },
1548 { 96, 200, 0.2, "four" },
1549 { 96, 200, 0.3, "lines" }
1551 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES}});
1555 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1557 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1558 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1561 { 96, 200, 0.0, "We" },
1562 { 96, 200, 0.1, "have" },
1563 { 96, 200, 0.2, "four" },
1565 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1566 BOOST_REQUIRE (notes.empty());
1570 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1572 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1573 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1576 { 96, 300, 0.0, "We" },
1577 { 96, 300, 0.1, "have" },
1578 { 150, 180, 0.2, "four" },
1579 { 150, 180, 0.3, "lines" }
1581 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES}});
1585 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1587 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1588 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1591 { 96, 300, 0.0, "We" },
1592 { 96, 300, 0.1, "have" },
1593 { 150, 180, 0.2, "four" },
1594 { 190, 250, 0.3, "lines" }
1596 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1597 BOOST_REQUIRE (notes.empty());
1601 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1603 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1604 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1607 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1609 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG }});
1613 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1615 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1616 prepare_directory (dir);
1618 auto picture = simple_picture (dir, "foo");
1619 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1620 auto reel = make_shared<dcp::Reel>();
1621 reel->add (reel_picture);
1622 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1623 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1624 reel->add (reel_sound);
1625 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1627 auto dcp = make_shared<dcp::DCP>(dir);
1629 dcp->write_xml (dcp::SMPTE);
1631 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE }});
1635 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1637 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1638 auto dcp = make_simple (dir);
1639 dcp->write_xml (dcp::SMPTE);
1640 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1643 BOOST_REQUIRE (dcp->cpls()[0]->file());
1644 Editor e(dcp->cpls()[0]->file().get());
1645 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1648 check_verify_result (
1651 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1652 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1657 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1659 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1660 auto dcp = make_simple (dir);
1661 dcp->write_xml (dcp::SMPTE);
1662 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1665 BOOST_REQUIRE (dcp->cpls()[0]->file());
1666 Editor e(dcp->cpls()[0]->file().get());
1667 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1670 check_verify_result (
1673 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1674 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1679 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1681 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1682 boost::filesystem::remove_all (dir);
1683 boost::filesystem::create_directories (dir);
1684 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1685 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1687 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1688 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1691 make_shared<dcp::Reel>(
1692 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1693 make_shared<dcp::ReelSoundAsset>(ms, 0)
1698 dcp->write_xml (dcp::SMPTE);
1700 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION }});
1707 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1709 boost::filesystem::remove_all (dir);
1710 boost::filesystem::create_directories (dir);
1711 auto dcp = make_shared<dcp::DCP>(dir);
1712 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1714 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1715 subs->set_language (dcp::LanguageTag("de-DE"));
1716 subs->set_start_time (dcp::Time());
1717 subs->add (simple_subtitle());
1718 subs->write (dir / "subs.mxf");
1719 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1721 auto reel1 = make_shared<dcp::Reel>(
1722 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1723 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1727 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1733 auto reel2 = make_shared<dcp::Reel>(
1734 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1735 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1739 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1745 dcp->write_xml (dcp::SMPTE);
1749 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1752 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1753 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1754 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS}});
1758 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1759 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1760 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1761 BOOST_REQUIRE (notes.empty());
1765 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1766 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1767 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1768 BOOST_REQUIRE (notes.empty());
1775 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1777 boost::filesystem::remove_all (dir);
1778 boost::filesystem::create_directories (dir);
1779 auto dcp = make_shared<dcp::DCP>(dir);
1780 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1782 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1783 subs->set_language (dcp::LanguageTag("de-DE"));
1784 subs->set_start_time (dcp::Time());
1785 subs->add (simple_subtitle());
1786 subs->write (dir / "subs.mxf");
1788 auto reel1 = make_shared<dcp::Reel>(
1789 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1790 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1793 for (int i = 0; i < caps_in_reel1; ++i) {
1794 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1799 auto reel2 = make_shared<dcp::Reel>(
1800 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1801 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1804 for (int i = 0; i < caps_in_reel2; ++i) {
1805 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1811 dcp->write_xml (dcp::SMPTE);
1816 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
1819 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
1820 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
1821 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER }});
1825 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
1826 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
1827 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1828 BOOST_REQUIRE (notes.empty());
1832 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
1833 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
1834 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1835 BOOST_REQUIRE (notes.empty());
1842 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
1844 boost::filesystem::remove_all (dir);
1845 boost::filesystem::create_directories (dir);
1846 auto dcp = make_shared<dcp::DCP>(dir);
1847 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1849 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1850 subs->set_language (dcp::LanguageTag("de-DE"));
1851 subs->set_start_time (dcp::Time());
1852 subs->add (simple_subtitle());
1853 subs->write (dir / "subs.mxf");
1854 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
1857 auto reel = make_shared<dcp::Reel>(
1858 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1859 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1862 reel->add (reel_text);
1867 dcp->write_xml (dcp::SMPTE);
1869 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, code }});
1873 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
1875 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1876 "build/test/verify_subtitle_entry_point_must_be_present",
1877 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
1878 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
1879 asset->unset_entry_point ();
1883 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1884 "build/test/verify_subtitle_entry_point_must_be_zero",
1885 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
1886 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
1887 asset->set_entry_point (4);
1891 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
1892 "build/test/verify_closed_caption_entry_point_must_be_present",
1893 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
1894 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
1895 asset->unset_entry_point ();
1899 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
1900 "build/test/verify_closed_caption_entry_point_must_be_zero",
1901 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
1902 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
1903 asset->set_entry_point (9);
1909 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
1913 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
1914 auto dcp = make_simple (dir);
1915 dcp->write_xml (dcp::SMPTE);
1916 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1919 BOOST_REQUIRE (dcp->cpls()[0]->file());
1920 Editor e(dcp->cpls()[0]->file().get());
1921 e.replace("<Hash>cb1OLhgHG9svy7G8hoTSPpltzhw=</Hash>", "");
1924 check_verify_result (
1927 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
1928 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }