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;
73 stage (string s, optional<boost::filesystem::path> p)
75 stages.push_back (make_pair (s, p));
85 prepare_directory (boost::filesystem::path path)
87 using namespace boost::filesystem;
89 create_directories (path);
93 static vector<boost::filesystem::path>
94 setup (int reference_number, int verify_test_number)
96 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
97 for (auto i: boost::filesystem::directory_iterator(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number))) {
98 boost::filesystem::copy_file (i.path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i.path().filename());
101 return { dcp::String::compose("build/test/verify_test%1", verify_test_number) };
108 write_dcp_with_single_asset (boost::filesystem::path dir, shared_ptr<dcp::ReelAsset> reel_asset, dcp::Standard standard = dcp::SMPTE)
110 auto reel = make_shared<dcp::Reel>();
111 reel->add (reel_asset);
112 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
114 auto dcp = make_shared<dcp::DCP>(dir);
116 dcp->write_xml (standard);
120 /** Class that can alter a file by searching and replacing strings within it.
121 * On destruction modifies the file whose name was given to the constructor.
126 Editor (boost::filesystem::path path)
129 _content = dcp::file_to_string (_path);
134 auto f = fopen(_path.string().c_str(), "w");
136 fwrite (_content.c_str(), _content.length(), 1, f);
140 void replace (string a, string b)
142 auto old_content = _content;
143 boost::algorithm::replace_all (_content, a, b);
144 BOOST_REQUIRE (_content != old_content);
148 boost::filesystem::path _path;
149 std::string _content;
155 dump_notes (vector<dcp::VerificationNote> const & notes)
157 for (auto i: notes) {
158 std::cout << dcp::note_to_string(i) << "\n";
165 check_verify_result (vector<boost::filesystem::path> dir, vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes)
167 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
169 BOOST_REQUIRE_EQUAL (notes.size(), types_and_codes.size());
170 auto i = notes.begin();
171 auto j = types_and_codes.begin();
172 while (i != notes.end()) {
173 BOOST_CHECK_EQUAL (i->type(), j->first);
174 BOOST_CHECK_EQUAL (i->code(), j->second);
183 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
185 auto directories = setup (1, n);
189 e.replace (from, to);
192 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
194 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
195 auto i = notes.begin();
196 auto j = codes.begin();
197 while (i != notes.end()) {
198 BOOST_CHECK_EQUAL (i->code(), *j);
205 /* Check DCP as-is (should be OK) */
206 BOOST_AUTO_TEST_CASE (verify_test1)
209 auto directories = setup (1, 1);
210 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
212 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
213 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
214 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
216 auto st = stages.begin();
217 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
218 BOOST_REQUIRE (st->second);
219 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
221 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
222 BOOST_REQUIRE (st->second);
223 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
225 BOOST_CHECK_EQUAL (st->first, "Checking reel");
226 BOOST_REQUIRE (!st->second);
228 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
229 BOOST_REQUIRE (st->second);
230 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
232 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
233 BOOST_REQUIRE (st->second);
234 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
236 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
237 BOOST_REQUIRE (st->second);
238 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
240 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
241 BOOST_REQUIRE (st->second);
242 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
244 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
245 BOOST_REQUIRE (st->second);
246 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
248 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
249 BOOST_REQUIRE (st->second);
250 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
252 BOOST_REQUIRE (st == stages.end());
254 BOOST_CHECK_EQUAL (notes.size(), 0);
257 /* Corrupt the MXFs and check that this is spotted */
258 BOOST_AUTO_TEST_CASE (verify_test2)
260 auto directories = setup (1, 2);
262 auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
264 fseek (mod, 4096, SEEK_SET);
266 fwrite (&x, sizeof(x), 1, mod);
269 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
271 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
272 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
275 dcp::ASDCPErrorSuspender sus;
276 check_verify_result (
279 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
280 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
284 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
285 BOOST_AUTO_TEST_CASE (verify_test3)
287 auto directories = setup (1, 3);
290 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
291 e.replace ("<Hash>", "<Hash>x");
294 check_verify_result (
297 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
298 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER },
299 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER },
300 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
301 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
302 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
306 /* Corrupt the ContentKind in the CPL */
307 BOOST_AUTO_TEST_CASE (verify_test4)
309 auto directories = setup (1, 4);
312 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
313 e.replace ("<ContentKind>", "<ContentKind>x");
316 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
318 BOOST_REQUIRE_EQUAL (notes.size(), 1);
319 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
320 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xtrailer'");
324 boost::filesystem::path
327 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
331 boost::filesystem::path
334 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
338 boost::filesystem::path
341 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
346 BOOST_AUTO_TEST_CASE (verify_test5)
348 check_verify_result_after_replace (
350 "<FrameRate>24 1", "<FrameRate>99 1",
351 { dcp::VerificationNote::CPL_HASH_INCORRECT,
352 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
357 BOOST_AUTO_TEST_CASE (verify_test6)
359 auto directories = setup (1, 6);
361 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
362 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
366 boost::filesystem::path
369 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
372 /* Empty asset filename in ASSETMAP */
373 BOOST_AUTO_TEST_CASE (verify_test7)
375 check_verify_result_after_replace (
377 "<Path>video.mxf</Path>", "<Path></Path>",
378 { dcp::VerificationNote::EMPTY_ASSET_PATH }
382 /* Mismatched standard */
383 BOOST_AUTO_TEST_CASE (verify_test8)
385 check_verify_result_after_replace (
387 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
388 { dcp::VerificationNote::MISMATCHED_STANDARD,
389 dcp::VerificationNote::XML_VALIDATION_ERROR,
390 dcp::VerificationNote::CPL_HASH_INCORRECT }
394 /* Badly formatted <Id> in CPL */
395 BOOST_AUTO_TEST_CASE (verify_test9)
397 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
398 check_verify_result_after_replace (
400 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
401 { dcp::VerificationNote::XML_VALIDATION_ERROR }
405 /* Badly formatted <IssueDate> in CPL */
406 BOOST_AUTO_TEST_CASE (verify_test10)
408 check_verify_result_after_replace (
410 "<IssueDate>", "<IssueDate>x",
411 { dcp::VerificationNote::XML_VALIDATION_ERROR,
412 dcp::VerificationNote::CPL_HASH_INCORRECT }
416 /* Badly-formatted <Id> in PKL */
417 BOOST_AUTO_TEST_CASE (verify_test11)
419 check_verify_result_after_replace (
421 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
422 { dcp::VerificationNote::XML_VALIDATION_ERROR }
426 /* Badly-formatted <Id> in ASSETMAP */
427 BOOST_AUTO_TEST_CASE (verify_test12)
429 check_verify_result_after_replace (
431 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
432 { dcp::VerificationNote::XML_VALIDATION_ERROR }
436 /* Basic test of an Interop DCP */
437 BOOST_AUTO_TEST_CASE (verify_test13)
440 auto directories = setup (3, 13);
441 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
443 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
444 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
445 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
447 auto st = stages.begin();
448 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
449 BOOST_REQUIRE (st->second);
450 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
452 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
453 BOOST_REQUIRE (st->second);
454 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
456 BOOST_CHECK_EQUAL (st->first, "Checking reel");
457 BOOST_REQUIRE (!st->second);
459 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
460 BOOST_REQUIRE (st->second);
461 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
463 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
464 BOOST_REQUIRE (st->second);
465 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
467 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
468 BOOST_REQUIRE (st->second);
469 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
471 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
472 BOOST_REQUIRE (st->second);
473 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
475 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
476 BOOST_REQUIRE (st->second);
477 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
479 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
480 BOOST_REQUIRE (st->second);
481 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
483 BOOST_REQUIRE (st == stages.end());
485 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
486 auto i = notes.begin ();
487 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
488 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
491 /* DCP with a short asset */
492 BOOST_AUTO_TEST_CASE (verify_test14)
494 auto directories = setup (8, 14);
495 check_verify_result (
498 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
499 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
500 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
501 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
502 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
509 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
511 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
512 boost::filesystem::create_directories (dir);
513 auto writer = asset->start_write (dir / "pic.mxf", true);
514 for (int i = 0; i < 24; ++i) {
515 writer->write (frame.data(), frame.size());
519 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
520 write_dcp_with_single_asset (dir, reel_asset);
524 /* DCP with an over-sized JPEG2000 frame */
525 BOOST_AUTO_TEST_CASE (verify_test15)
527 int const too_big = 1302083 * 2;
529 /* Compress a black image */
530 auto image = black_image ();
531 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
532 BOOST_REQUIRE (frame.size() < too_big);
534 /* Place it in a bigger block with some zero padding at the end */
535 dcp::ArrayData oversized_frame(too_big);
536 memcpy (oversized_frame.data(), frame.data(), frame.size());
537 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
539 boost::filesystem::path const dir("build/test/verify_test15");
540 boost::filesystem::remove_all (dir);
541 dcp_from_frame (oversized_frame, dir);
543 check_verify_result (
545 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES }}
550 /* DCP with a nearly over-sized JPEG2000 frame */
551 BOOST_AUTO_TEST_CASE (verify_test16)
553 int const nearly_too_big = 1302083 * 0.98;
555 /* Compress a black image */
556 auto image = black_image ();
557 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
558 BOOST_REQUIRE (frame.size() < nearly_too_big);
560 /* Place it in a bigger block with some zero padding at the end */
561 dcp::ArrayData oversized_frame(nearly_too_big);
562 memcpy (oversized_frame.data(), frame.data(), frame.size());
563 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
565 boost::filesystem::path const dir("build/test/verify_test16");
566 boost::filesystem::remove_all (dir);
567 dcp_from_frame (oversized_frame, dir);
569 check_verify_result (
571 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES }}
576 /* DCP with a within-range JPEG2000 frame */
577 BOOST_AUTO_TEST_CASE (verify_test17)
579 /* Compress a black image */
580 auto image = black_image ();
581 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
582 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
584 boost::filesystem::path const dir("build/test/verify_test17");
585 boost::filesystem::remove_all (dir);
586 dcp_from_frame (frame, dir);
588 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
589 BOOST_REQUIRE_EQUAL (notes.size(), 0);
593 /* DCP with valid Interop subtitles */
594 BOOST_AUTO_TEST_CASE (verify_test18)
596 boost::filesystem::path const dir("build/test/verify_test18");
597 prepare_directory (dir);
598 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
599 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
600 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
601 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
603 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
607 /* DCP with broken Interop subtitles */
608 BOOST_AUTO_TEST_CASE (verify_test19)
610 boost::filesystem::path const dir("build/test/verify_test19");
611 prepare_directory (dir);
612 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
613 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
614 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
615 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
618 Editor e (dir / "subs.xml");
619 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
622 check_verify_result (
625 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
626 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
627 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
632 /* DCP with valid SMPTE subtitles */
633 BOOST_AUTO_TEST_CASE (verify_test20)
635 boost::filesystem::path const dir("build/test/verify_test20");
636 prepare_directory (dir);
637 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
638 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
639 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
640 write_dcp_with_single_asset (dir, reel_asset);
642 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
643 BOOST_REQUIRE_EQUAL (notes.size(), 0);
647 /* DCP with broken SMPTE subtitles */
648 BOOST_AUTO_TEST_CASE (verify_test21)
650 boost::filesystem::path const dir("build/test/verify_test21");
651 prepare_directory (dir);
652 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
653 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
654 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
655 write_dcp_with_single_asset (dir, reel_asset);
657 check_verify_result (
660 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
661 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
662 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
668 BOOST_AUTO_TEST_CASE (verify_test22)
670 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
671 prepare_directory (ov_dir);
673 auto image = black_image ();
674 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
675 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
676 dcp_from_frame (frame, ov_dir);
678 dcp::DCP ov (ov_dir);
681 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
682 prepare_directory (vf_dir);
684 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
686 check_verify_result (
688 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET }});
692 /* DCP with valid CompositionMetadataAsset */
693 BOOST_AUTO_TEST_CASE (verify_test23)
695 boost::filesystem::path const dir("build/test/verify_test23");
696 prepare_directory (dir);
698 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
699 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
700 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
702 auto reel = make_shared<dcp::Reel>();
703 reel->add (reel_asset);
704 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
706 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
707 cpl->set_main_sound_sample_rate (48000);
708 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
709 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
713 dcp.write_xml (dcp::SMPTE);
715 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
716 BOOST_CHECK (notes.empty());
720 boost::filesystem::path find_cpl (boost::filesystem::path dir)
722 for (auto i: boost::filesystem::directory_iterator(dir)) {
723 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
728 BOOST_REQUIRE (false);
733 /* DCP with invalid CompositionMetadataAsset */
734 BOOST_AUTO_TEST_CASE (verify_test24)
736 boost::filesystem::path const dir("build/test/verify_test24");
737 prepare_directory (dir);
739 auto reel = make_shared<dcp::Reel>();
740 reel->add (black_picture_asset(dir));
741 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
743 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
744 cpl->set_main_sound_sample_rate (48000);
745 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
746 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
750 dcp.write_xml (dcp::SMPTE);
753 Editor e (find_cpl("build/test/verify_test24"));
754 e.replace ("MainSound", "MainSoundX");
757 check_verify_result (
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::XML_VALIDATION_ERROR },
763 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
768 /* DCP with invalid CompositionMetadataAsset */
769 BOOST_AUTO_TEST_CASE (verify_test25)
771 boost::filesystem::path const dir("build/test/verify_test25");
772 prepare_directory (dir);
774 auto reel = make_shared<dcp::Reel>();
775 reel->add (black_picture_asset(dir));
776 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
778 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
779 cpl->set_main_sound_sample_rate (48000);
780 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
781 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
785 dcp.write_xml (dcp::SMPTE);
788 Editor e (find_cpl("build/test/verify_test25"));
789 e.replace ("meta:Width", "meta:WidthX");
792 check_verify_result (
794 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
799 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
800 BOOST_AUTO_TEST_CASE (verify_test26)
802 boost::filesystem::path const dir("build/test/verify_test26");
803 prepare_directory (dir);
804 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
805 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
806 asset->_language = "wrong-andbad";
807 asset->write (dir / "subs.mxf");
808 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
809 reel_asset->_language = "badlang";
810 write_dcp_with_single_asset (dir, reel_asset);
812 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
813 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
814 auto i = notes.begin();
815 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
816 BOOST_REQUIRE (i->note());
817 BOOST_CHECK_EQUAL (*i->note(), "badlang");
819 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
820 BOOST_REQUIRE (i->note());
821 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
825 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
826 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
828 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
829 prepare_directory (dir);
830 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
831 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
832 asset->_language = "wrong-andbad";
833 asset->write (dir / "subs.mxf");
834 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
835 reel_asset->_language = "badlang";
836 write_dcp_with_single_asset (dir, reel_asset);
838 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
839 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
840 auto i = notes.begin ();
841 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
842 BOOST_REQUIRE (i->note());
843 BOOST_CHECK_EQUAL (*i->note(), "badlang");
845 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
846 BOOST_REQUIRE (i->note());
847 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
851 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
852 * the release territory.
854 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
856 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
857 prepare_directory (dir);
859 auto picture = simple_picture (dir, "foo");
860 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
861 auto reel = make_shared<dcp::Reel>();
862 reel->add (reel_picture);
863 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
864 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
865 reel->add (reel_sound);
866 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
868 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
869 cpl->_additional_subtitle_languages.push_back("andso-is-this");
870 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
871 cpl->set_main_sound_sample_rate (48000);
872 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
873 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
874 cpl->_release_territory = "fred-jim";
875 auto dcp = make_shared<dcp::DCP>(dir);
877 dcp->write_xml (dcp::SMPTE);
879 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
880 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
881 auto i = notes.begin ();
882 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
883 BOOST_REQUIRE (i->note());
884 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
886 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
887 BOOST_REQUIRE (i->note());
888 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
890 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
891 BOOST_REQUIRE (i->note());
892 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
894 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
895 BOOST_REQUIRE (i->note());
896 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
902 vector<dcp::VerificationNote>
903 check_picture_size (int width, int height, int frame_rate, bool three_d)
905 using namespace boost::filesystem;
907 path dcp_path = "build/test/verify_picture_test";
908 remove_all (dcp_path);
909 create_directories (dcp_path);
911 shared_ptr<dcp::PictureAsset> mp;
913 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
915 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
917 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
919 auto image = black_image (dcp::Size(width, height));
920 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
921 int const length = three_d ? frame_rate * 2 : frame_rate;
922 for (int i = 0; i < length; ++i) {
923 picture_writer->write (j2c.data(), j2c.size());
925 picture_writer->finalize ();
927 auto d = make_shared<dcp::DCP>(dcp_path);
928 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
929 cpl->set_annotation_text ("A Test DCP");
930 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
932 auto reel = make_shared<dcp::Reel>();
935 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
937 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
943 d->write_xml (dcp::SMPTE);
945 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
951 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
953 auto notes = check_picture_size(width, height, frame_rate, three_d);
955 BOOST_CHECK_EQUAL (notes.size(), 0U);
961 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
963 auto notes = check_picture_size(width, height, frame_rate, three_d);
964 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
965 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
966 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
972 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
974 auto notes = check_picture_size(width, height, frame_rate, three_d);
975 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
976 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
977 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
983 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
985 auto notes = check_picture_size(width, height, frame_rate, three_d);
986 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
987 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
988 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
992 BOOST_AUTO_TEST_CASE (verify_picture_size)
994 using namespace boost::filesystem;
997 check_picture_size_ok (2048, 858, 24, false);
998 check_picture_size_ok (2048, 858, 25, false);
999 check_picture_size_ok (2048, 858, 48, false);
1000 check_picture_size_ok (2048, 858, 24, true);
1001 check_picture_size_ok (2048, 858, 25, true);
1002 check_picture_size_ok (2048, 858, 48, true);
1005 check_picture_size_ok (1998, 1080, 24, false);
1006 check_picture_size_ok (1998, 1080, 25, false);
1007 check_picture_size_ok (1998, 1080, 48, false);
1008 check_picture_size_ok (1998, 1080, 24, true);
1009 check_picture_size_ok (1998, 1080, 25, true);
1010 check_picture_size_ok (1998, 1080, 48, true);
1013 check_picture_size_ok (4096, 1716, 24, false);
1016 check_picture_size_ok (3996, 2160, 24, false);
1018 /* Bad frame size */
1019 check_picture_size_bad_frame_size (2050, 858, 24, false);
1020 check_picture_size_bad_frame_size (2048, 658, 25, false);
1021 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1022 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1024 /* Bad 2K frame rate */
1025 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1026 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1027 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1029 /* Bad 4K frame rate */
1030 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1031 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1034 auto notes = check_picture_size(3996, 2160, 24, true);
1035 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1036 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1037 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1043 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1046 make_shared<dcp::SubtitleString>(
1054 dcp::Time(start_frame, 24, 24),
1055 dcp::Time(end_frame, 24, 24),
1071 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1073 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1074 prepare_directory (dir);
1076 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1077 for (int i = 0; i < 2048; ++i) {
1078 add_test_subtitle (asset, i * 24, i * 24 + 20);
1080 asset->set_language (dcp::LanguageTag("de-DE"));
1081 asset->write (dir / "subs.mxf");
1082 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1083 write_dcp_with_single_asset (dir, reel_asset);
1085 check_verify_result (
1088 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1089 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1090 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1096 shared_ptr<dcp::SMPTESubtitleAsset>
1097 make_large_subtitle_asset (boost::filesystem::path font_file)
1099 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1100 dcp::ArrayData big_fake_font(1024 * 1024);
1101 big_fake_font.write (font_file);
1102 for (int i = 0; i < 116; ++i) {
1103 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1111 verify_timed_text_asset_too_large (string name)
1113 auto const dir = boost::filesystem::path("build/test") / name;
1114 prepare_directory (dir);
1115 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1116 add_test_subtitle (asset, 0, 20);
1117 asset->set_language (dcp::LanguageTag("de-DE"));
1118 asset->write (dir / "subs.mxf");
1120 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1121 write_dcp_with_single_asset (dir, reel_asset);
1123 check_verify_result (
1126 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1127 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1128 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1129 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1134 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1136 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1137 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1141 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1143 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1144 prepare_directory (dir);
1145 auto dcp = make_simple (dir, 1, 240);
1148 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1149 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1150 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1151 "<ContentTitleText>Content</ContentTitleText>"
1152 "<AnnotationText>Annotation</AnnotationText>"
1153 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1154 "<ReelNumber>1</ReelNumber>"
1155 "<EditRate>25 1</EditRate>"
1156 "<TimeCodeRate>25</TimeCodeRate>"
1157 "<StartTime>00:00:00:00</StartTime>"
1158 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1160 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1161 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1162 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1168 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1169 BOOST_REQUIRE (xml_file);
1170 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1172 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1173 subs->write (dir / "subs.mxf");
1175 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1176 dcp->cpls().front()->reels().front()->add(reel_subs);
1177 dcp->write_xml (dcp::SMPTE);
1179 check_verify_result (
1182 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1183 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1188 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1190 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1191 auto dcp = make_simple (path, 2, 240);
1192 auto cpl = dcp->cpls()[0];
1195 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1196 subs->set_language (dcp::LanguageTag("de-DE"));
1197 subs->add (simple_subtitle());
1198 subs->write (path / "subs1.mxf");
1199 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1200 cpl->reels()[0]->add(reel_subs);
1204 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1205 subs->set_language (dcp::LanguageTag("en-US"));
1206 subs->add (simple_subtitle());
1207 subs->write (path / "subs2.mxf");
1208 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1209 cpl->reels()[1]->add(reel_subs);
1212 dcp->write_xml (dcp::SMPTE);
1214 check_verify_result (
1217 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1218 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1219 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1224 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1226 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1227 prepare_directory (dir);
1228 auto dcp = make_simple (dir, 1, 240);
1231 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1232 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1233 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1234 "<ContentTitleText>Content</ContentTitleText>"
1235 "<AnnotationText>Annotation</AnnotationText>"
1236 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1237 "<ReelNumber>1</ReelNumber>"
1238 "<Language>de-DE</Language>"
1239 "<EditRate>25 1</EditRate>"
1240 "<TimeCodeRate>25</TimeCodeRate>"
1241 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1243 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1244 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1245 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1251 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1252 BOOST_REQUIRE (xml_file);
1253 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1255 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1256 subs->write (dir / "subs.mxf");
1258 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1259 dcp->cpls().front()->reels().front()->add(reel_subs);
1260 dcp->write_xml (dcp::SMPTE);
1262 check_verify_result (
1265 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1266 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1271 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1273 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1274 prepare_directory (dir);
1275 auto dcp = make_simple (dir, 1, 240);
1278 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1279 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1280 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1281 "<ContentTitleText>Content</ContentTitleText>"
1282 "<AnnotationText>Annotation</AnnotationText>"
1283 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1284 "<ReelNumber>1</ReelNumber>"
1285 "<Language>de-DE</Language>"
1286 "<EditRate>25 1</EditRate>"
1287 "<TimeCodeRate>25</TimeCodeRate>"
1288 "<StartTime>00:00:02:00</StartTime>"
1289 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1291 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1292 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1293 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1299 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1300 BOOST_REQUIRE (xml_file);
1301 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1303 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1304 subs->write (dir / "subs.mxf");
1306 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1307 dcp->cpls().front()->reels().front()->add(reel_subs);
1308 dcp->write_xml (dcp::SMPTE);
1310 check_verify_result (
1313 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1314 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1322 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1325 , v_position(v_position_)
1338 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1340 prepare_directory (dir);
1341 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1342 asset->set_start_time (dcp::Time());
1343 for (auto i: subs) {
1344 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1346 asset->set_language (dcp::LanguageTag("de-DE"));
1347 asset->write (dir / "subs.mxf");
1349 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1350 write_dcp_with_single_asset (dir, reel_asset);
1354 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1356 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1357 /* Just too early */
1358 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1359 check_verify_result (
1361 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }});
1365 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1367 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1368 /* Just late enough */
1369 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1370 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1371 BOOST_REQUIRE (notes.empty());
1375 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1377 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1378 prepare_directory (dir);
1380 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1381 asset1->set_start_time (dcp::Time());
1382 /* Just late enough */
1383 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1384 asset1->set_language (dcp::LanguageTag("de-DE"));
1385 asset1->write (dir / "subs1.mxf");
1386 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1387 auto reel1 = make_shared<dcp::Reel>();
1388 reel1->add (reel_asset1);
1390 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1391 asset2->set_start_time (dcp::Time());
1392 /* This would be too early on first reel but should be OK on the second */
1393 add_test_subtitle (asset2, 0, 4 * 24);
1394 asset2->set_language (dcp::LanguageTag("de-DE"));
1395 asset2->write (dir / "subs2.mxf");
1396 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1397 auto reel2 = make_shared<dcp::Reel>();
1398 reel2->add (reel_asset2);
1400 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1403 auto dcp = make_shared<dcp::DCP>(dir);
1405 dcp->write_xml (dcp::SMPTE);
1407 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1408 BOOST_REQUIRE (notes.empty());
1412 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1414 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1415 dcp_with_text<dcp::ReelSubtitleAsset> (
1419 { 5 * 24 + 1, 6 * 24 },
1421 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE }});
1425 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1427 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1428 dcp_with_text<dcp::ReelSubtitleAsset> (
1432 { 5 * 24 + 16, 8 * 24 },
1434 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1435 BOOST_REQUIRE (notes.empty());
1439 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1441 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1442 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1443 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT }});
1447 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1449 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1450 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1451 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1452 BOOST_REQUIRE (notes.empty());
1456 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1458 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines1");
1459 dcp_with_text<dcp::ReelSubtitleAsset> (
1462 { 96, 200, 0.0, "We" },
1463 { 96, 200, 0.1, "have" },
1464 { 96, 200, 0.2, "four" },
1465 { 96, 200, 0.3, "lines" }
1467 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES}});
1471 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1473 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1474 dcp_with_text<dcp::ReelSubtitleAsset> (
1477 { 96, 200, 0.0, "We" },
1478 { 96, 200, 0.1, "have" },
1479 { 96, 200, 0.2, "four" },
1481 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1482 BOOST_REQUIRE (notes.empty());
1486 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1488 auto const dir = boost::filesystem::path ("build/test/verify_too_many_subtitle_lines2");
1489 dcp_with_text<dcp::ReelSubtitleAsset> (
1492 { 96, 300, 0.0, "We" },
1493 { 96, 300, 0.1, "have" },
1494 { 150, 180, 0.2, "four" },
1495 { 150, 180, 0.3, "lines" }
1497 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES}});
1501 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1503 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1504 dcp_with_text<dcp::ReelSubtitleAsset> (
1507 { 96, 300, 0.0, "We" },
1508 { 96, 300, 0.1, "have" },
1509 { 150, 180, 0.2, "four" },
1510 { 190, 250, 0.3, "lines" }
1512 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1513 BOOST_REQUIRE (notes.empty());
1517 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1519 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1520 dcp_with_text<dcp::ReelSubtitleAsset> (
1523 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1525 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED }});
1529 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1531 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1532 dcp_with_text<dcp::ReelSubtitleAsset> (
1535 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1537 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG }});
1541 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1543 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines1");
1544 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1547 { 96, 200, 0.0, "We" },
1548 { 96, 200, 0.1, "have" },
1549 { 96, 200, 0.2, "four" },
1550 { 96, 200, 0.3, "lines" }
1552 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES}});
1556 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1558 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1559 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1562 { 96, 200, 0.0, "We" },
1563 { 96, 200, 0.1, "have" },
1564 { 96, 200, 0.2, "four" },
1566 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1567 BOOST_REQUIRE (notes.empty());
1571 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1573 auto const dir = boost::filesystem::path ("build/test/verify_too_many_closed_caption_lines2");
1574 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1577 { 96, 300, 0.0, "We" },
1578 { 96, 300, 0.1, "have" },
1579 { 150, 180, 0.2, "four" },
1580 { 150, 180, 0.3, "lines" }
1582 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES}});
1586 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1588 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1589 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1592 { 96, 300, 0.0, "We" },
1593 { 96, 300, 0.1, "have" },
1594 { 150, 180, 0.2, "four" },
1595 { 190, 250, 0.3, "lines" }
1597 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1598 BOOST_REQUIRE (notes.empty());
1602 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1604 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1605 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1608 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1610 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG }});
1614 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1616 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1617 prepare_directory (dir);
1619 auto picture = simple_picture (dir, "foo");
1620 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1621 auto reel = make_shared<dcp::Reel>();
1622 reel->add (reel_picture);
1623 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1624 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1625 reel->add (reel_sound);
1626 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1628 auto dcp = make_shared<dcp::DCP>(dir);
1630 dcp->write_xml (dcp::SMPTE);
1632 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE }});
1636 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1638 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1639 auto dcp = make_simple (dir);
1640 dcp->write_xml (dcp::SMPTE);
1641 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1644 BOOST_REQUIRE (dcp->cpls()[0]->file());
1645 Editor e(dcp->cpls()[0]->file().get());
1646 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1649 check_verify_result (
1652 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1653 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1658 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1660 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1661 auto dcp = make_simple (dir);
1662 dcp->write_xml (dcp::SMPTE);
1663 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1666 BOOST_REQUIRE (dcp->cpls()[0]->file());
1667 Editor e(dcp->cpls()[0]->file().get());
1668 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1671 check_verify_result (
1674 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1675 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1680 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1682 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1683 boost::filesystem::remove_all (dir);
1684 boost::filesystem::create_directories (dir);
1685 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1686 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1688 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1689 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1692 make_shared<dcp::Reel>(
1693 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1694 make_shared<dcp::ReelSoundAsset>(ms, 0)
1699 dcp->write_xml (dcp::SMPTE);
1701 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION }});
1708 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1710 boost::filesystem::remove_all (dir);
1711 boost::filesystem::create_directories (dir);
1712 auto dcp = make_shared<dcp::DCP>(dir);
1713 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1715 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1716 subs->set_language (dcp::LanguageTag("de-DE"));
1717 subs->set_start_time (dcp::Time());
1718 subs->add (simple_subtitle());
1719 subs->write (dir / "subs.mxf");
1720 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1722 auto reel1 = make_shared<dcp::Reel>(
1723 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1724 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1728 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1734 auto reel2 = make_shared<dcp::Reel>(
1735 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1736 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1740 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1746 dcp->write_xml (dcp::SMPTE);
1750 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1753 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1754 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1755 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS}});
1759 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1760 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1761 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1762 BOOST_REQUIRE (notes.empty());
1766 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1767 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1768 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1769 BOOST_REQUIRE (notes.empty());
1776 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1778 boost::filesystem::remove_all (dir);
1779 boost::filesystem::create_directories (dir);
1780 auto dcp = make_shared<dcp::DCP>(dir);
1781 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1783 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1784 subs->set_language (dcp::LanguageTag("de-DE"));
1785 subs->set_start_time (dcp::Time());
1786 subs->add (simple_subtitle());
1787 subs->write (dir / "subs.mxf");
1789 auto reel1 = make_shared<dcp::Reel>(
1790 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1791 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1794 for (int i = 0; i < caps_in_reel1; ++i) {
1795 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1800 auto reel2 = make_shared<dcp::Reel>(
1801 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1802 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1805 for (int i = 0; i < caps_in_reel2; ++i) {
1806 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1812 dcp->write_xml (dcp::SMPTE);
1817 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
1820 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
1821 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
1822 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER }});
1826 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
1827 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
1828 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1829 BOOST_REQUIRE (notes.empty());
1833 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
1834 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
1835 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1836 BOOST_REQUIRE (notes.empty());
1843 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
1845 boost::filesystem::remove_all (dir);
1846 boost::filesystem::create_directories (dir);
1847 auto dcp = make_shared<dcp::DCP>(dir);
1848 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1850 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1851 subs->set_language (dcp::LanguageTag("de-DE"));
1852 subs->set_start_time (dcp::Time());
1853 subs->add (simple_subtitle());
1854 subs->write (dir / "subs.mxf");
1855 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
1858 auto reel = make_shared<dcp::Reel>(
1859 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1860 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1863 reel->add (reel_text);
1868 dcp->write_xml (dcp::SMPTE);
1870 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, code }});
1874 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
1876 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1877 "build/test/verify_subtitle_entry_point_must_be_present",
1878 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
1879 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
1880 asset->unset_entry_point ();
1884 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1885 "build/test/verify_subtitle_entry_point_must_be_zero",
1886 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
1887 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
1888 asset->set_entry_point (4);
1892 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
1893 "build/test/verify_closed_caption_entry_point_must_be_present",
1894 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
1895 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
1896 asset->unset_entry_point ();
1900 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
1901 "build/test/verify_closed_caption_entry_point_must_be_zero",
1902 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
1903 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
1904 asset->set_entry_point (9);
1910 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
1914 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
1915 auto dcp = make_simple (dir);
1916 dcp->write_xml (dcp::SMPTE);
1917 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1920 BOOST_REQUIRE (dcp->cpls()[0]->file());
1921 Editor e(dcp->cpls()[0]->file().get());
1922 e.replace("<Hash>cb1OLhgHG9svy7G8hoTSPpltzhw=</Hash>", "");
1925 check_verify_result (
1928 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
1929 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
1935 BOOST_AUTO_TEST_CASE (verify_features_must_have_ffec_ffmc1)
1937 boost::filesystem::path const dir("build/test/verify_features_must_have_ffec_ffmc1");
1938 auto dcp = make_simple (dir);
1939 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
1940 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 0);
1941 markers->set (dcp::Marker::FFEC, dcp::Time(0, 0, 4, 0, 24));
1942 markers->set (dcp::Marker::FFMC, dcp::Time(0, 0, 5, 0, 24));
1943 dcp->cpls()[0]->reels()[0]->add(markers);
1944 dcp->write_xml (dcp::SMPTE);
1945 check_verify_result ({dir}, {});
1949 BOOST_AUTO_TEST_CASE (verify_features_must_have_ffec_ffmc2)
1951 boost::filesystem::path const dir("build/test/verify_features_must_have_ffec_ffmc2");
1952 auto dcp = make_simple (dir);
1953 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
1954 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 0);
1955 markers->set (dcp::Marker::FFEC, dcp::Time(0, 0, 4, 0, 24));
1956 dcp->cpls()[0]->reels()[0]->add(markers);
1957 dcp->write_xml (dcp::SMPTE);
1958 check_verify_result ({dir}, {{dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE}});
1962 BOOST_AUTO_TEST_CASE (verify_features_must_have_ffec_ffmc3)
1964 boost::filesystem::path const dir("build/test/verify_features_must_have_ffec_ffmc3");
1965 auto dcp = make_simple (dir);
1966 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
1967 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 0);
1968 markers->set (dcp::Marker::FFMC, dcp::Time(0, 0, 4, 0, 24));
1969 dcp->cpls()[0]->reels()[0]->add(markers);
1970 dcp->write_xml (dcp::SMPTE);
1971 check_verify_result ({dir}, {{dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE}});
1975 BOOST_AUTO_TEST_CASE (verify_features_must_have_ffec_ffmc4)
1977 boost::filesystem::path const dir("build/test/verify_features_must_have_ffec_ffmc4");
1978 auto dcp = make_simple (dir);
1979 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
1980 auto markers = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 0);
1981 dcp->cpls()[0]->reels()[0]->add(markers);
1982 dcp->write_xml (dcp::SMPTE);
1983 check_verify_result (
1986 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE },
1987 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }