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 reel->add (simple_markers());
114 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
116 auto dcp = make_shared<dcp::DCP>(dir);
118 dcp->write_xml (standard);
122 /** Class that can alter a file by searching and replacing strings within it.
123 * On destruction modifies the file whose name was given to the constructor.
128 Editor (boost::filesystem::path path)
131 _content = dcp::file_to_string (_path);
136 auto f = fopen(_path.string().c_str(), "w");
138 fwrite (_content.c_str(), _content.length(), 1, f);
142 void replace (string a, string b)
144 auto old_content = _content;
145 boost::algorithm::replace_all (_content, a, b);
146 BOOST_REQUIRE (_content != old_content);
150 boost::filesystem::path _path;
151 std::string _content;
157 dump_notes (vector<dcp::VerificationNote> const & notes)
159 for (auto i: notes) {
160 std::cout << dcp::note_to_string(i) << "\n";
167 check_verify_result (vector<boost::filesystem::path> dir, vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes)
169 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
171 BOOST_REQUIRE_EQUAL (notes.size(), types_and_codes.size());
172 auto i = notes.begin();
173 auto j = types_and_codes.begin();
174 while (i != notes.end()) {
175 BOOST_CHECK_EQUAL (i->type(), j->first);
176 BOOST_CHECK_EQUAL (i->code(), j->second);
185 check_verify_result_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, vector<dcp::VerificationNote::Code> codes)
187 auto directories = setup (1, n);
191 e.replace (from, to);
194 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
196 BOOST_REQUIRE_EQUAL (notes.size(), codes.size());
197 auto i = notes.begin();
198 auto j = codes.begin();
199 while (i != notes.end()) {
200 BOOST_CHECK_EQUAL (i->code(), *j);
207 /* Check DCP as-is (should be OK) */
208 BOOST_AUTO_TEST_CASE (verify_test1)
211 auto directories = setup (1, 1);
212 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
214 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
215 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_63c3aece-c581-4603-b612-75e43f0c0430.xml";
216 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
218 auto st = stages.begin();
219 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
220 BOOST_REQUIRE (st->second);
221 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
223 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
224 BOOST_REQUIRE (st->second);
225 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
227 BOOST_CHECK_EQUAL (st->first, "Checking reel");
228 BOOST_REQUIRE (!st->second);
230 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
231 BOOST_REQUIRE (st->second);
232 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
234 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
235 BOOST_REQUIRE (st->second);
236 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
238 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
239 BOOST_REQUIRE (st->second);
240 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
242 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
243 BOOST_REQUIRE (st->second);
244 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
246 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
247 BOOST_REQUIRE (st->second);
248 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
250 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
251 BOOST_REQUIRE (st->second);
252 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
254 BOOST_REQUIRE (st == stages.end());
256 BOOST_CHECK_EQUAL (notes.size(), 0);
259 /* Corrupt the MXFs and check that this is spotted */
260 BOOST_AUTO_TEST_CASE (verify_test2)
262 auto directories = setup (1, 2);
264 auto mod = fopen("build/test/verify_test2/video.mxf", "r+b");
266 fseek (mod, 4096, SEEK_SET);
268 fwrite (&x, sizeof(x), 1, mod);
271 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
273 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
274 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
277 dcp::ASDCPErrorSuspender sus;
278 check_verify_result (
281 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_HASH_INCORRECT },
282 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::SOUND_HASH_INCORRECT }
286 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
287 BOOST_AUTO_TEST_CASE (verify_test3)
289 auto directories = setup (1, 3);
292 Editor e ("build/test/verify_test3/pkl_63c3aece-c581-4603-b612-75e43f0c0430.xml");
293 e.replace ("<Hash>", "<Hash>x");
296 check_verify_result (
299 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
300 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER },
301 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER },
302 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
303 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
304 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
308 /* Corrupt the ContentKind in the CPL */
309 BOOST_AUTO_TEST_CASE (verify_test4)
311 auto directories = setup (1, 4);
314 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
315 e.replace ("<ContentKind>", "<ContentKind>x");
318 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
320 BOOST_REQUIRE_EQUAL (notes.size(), 1);
321 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
322 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xtrailer'");
326 boost::filesystem::path
329 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
333 boost::filesystem::path
336 return dcp::String::compose("build/test/verify_test%1/pkl_63c3aece-c581-4603-b612-75e43f0c0430.xml", n);
340 boost::filesystem::path
343 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
348 BOOST_AUTO_TEST_CASE (verify_test5)
350 check_verify_result_after_replace (
352 "<FrameRate>24 1", "<FrameRate>99 1",
353 { dcp::VerificationNote::CPL_HASH_INCORRECT,
354 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE }
359 BOOST_AUTO_TEST_CASE (verify_test6)
361 auto directories = setup (1, 6);
363 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
364 check_verify_result (directories, {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::MISSING_ASSET }});
368 boost::filesystem::path
371 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
374 /* Empty asset filename in ASSETMAP */
375 BOOST_AUTO_TEST_CASE (verify_test7)
377 check_verify_result_after_replace (
379 "<Path>video.mxf</Path>", "<Path></Path>",
380 { dcp::VerificationNote::EMPTY_ASSET_PATH }
384 /* Mismatched standard */
385 BOOST_AUTO_TEST_CASE (verify_test8)
387 check_verify_result_after_replace (
389 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
390 { dcp::VerificationNote::MISMATCHED_STANDARD,
391 dcp::VerificationNote::XML_VALIDATION_ERROR,
392 dcp::VerificationNote::CPL_HASH_INCORRECT }
396 /* Badly formatted <Id> in CPL */
397 BOOST_AUTO_TEST_CASE (verify_test9)
399 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
400 check_verify_result_after_replace (
402 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
403 { dcp::VerificationNote::XML_VALIDATION_ERROR }
407 /* Badly formatted <IssueDate> in CPL */
408 BOOST_AUTO_TEST_CASE (verify_test10)
410 check_verify_result_after_replace (
412 "<IssueDate>", "<IssueDate>x",
413 { dcp::VerificationNote::XML_VALIDATION_ERROR,
414 dcp::VerificationNote::CPL_HASH_INCORRECT }
418 /* Badly-formatted <Id> in PKL */
419 BOOST_AUTO_TEST_CASE (verify_test11)
421 check_verify_result_after_replace (
423 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
424 { dcp::VerificationNote::XML_VALIDATION_ERROR }
428 /* Badly-formatted <Id> in ASSETMAP */
429 BOOST_AUTO_TEST_CASE (verify_test12)
431 check_verify_result_after_replace (
433 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
434 { dcp::VerificationNote::XML_VALIDATION_ERROR }
438 /* Basic test of an Interop DCP */
439 BOOST_AUTO_TEST_CASE (verify_test13)
442 auto directories = setup (3, 13);
443 auto notes = dcp::verify (directories, &stage, &progress, xsd_test);
445 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
446 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
447 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
449 auto st = stages.begin();
450 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
451 BOOST_REQUIRE (st->second);
452 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
454 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
455 BOOST_REQUIRE (st->second);
456 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
458 BOOST_CHECK_EQUAL (st->first, "Checking reel");
459 BOOST_REQUIRE (!st->second);
461 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
462 BOOST_REQUIRE (st->second);
463 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
465 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
466 BOOST_REQUIRE (st->second);
467 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
469 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
470 BOOST_REQUIRE (st->second);
471 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
473 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
474 BOOST_REQUIRE (st->second);
475 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
477 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
478 BOOST_REQUIRE (st->second);
479 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
481 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
482 BOOST_REQUIRE (st->second);
483 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
485 BOOST_REQUIRE (st == stages.end());
487 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
488 auto i = notes.begin ();
489 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
490 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
493 /* DCP with a short asset */
494 BOOST_AUTO_TEST_CASE (verify_test14)
496 auto directories = setup (8, 14);
497 check_verify_result (
500 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
501 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
502 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL },
503 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::DURATION_TOO_SMALL },
504 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL }
511 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
513 auto asset = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(24, 1), dcp::SMPTE);
514 boost::filesystem::create_directories (dir);
515 auto writer = asset->start_write (dir / "pic.mxf", true);
516 for (int i = 0; i < 24; ++i) {
517 writer->write (frame.data(), frame.size());
521 auto reel_asset = make_shared<dcp::ReelMonoPictureAsset>(asset, 0);
522 write_dcp_with_single_asset (dir, reel_asset);
526 /* DCP with an over-sized JPEG2000 frame */
527 BOOST_AUTO_TEST_CASE (verify_test15)
529 int const too_big = 1302083 * 2;
531 /* Compress a black image */
532 auto image = black_image ();
533 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
534 BOOST_REQUIRE (frame.size() < too_big);
536 /* Place it in a bigger block with some zero padding at the end */
537 dcp::ArrayData oversized_frame(too_big);
538 memcpy (oversized_frame.data(), frame.data(), frame.size());
539 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
541 boost::filesystem::path const dir("build/test/verify_test15");
542 boost::filesystem::remove_all (dir);
543 dcp_from_frame (oversized_frame, dir);
545 check_verify_result (
547 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES }}
552 /* DCP with a nearly over-sized JPEG2000 frame */
553 BOOST_AUTO_TEST_CASE (verify_test16)
555 int const nearly_too_big = 1302083 * 0.98;
557 /* Compress a black image */
558 auto image = black_image ();
559 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
560 BOOST_REQUIRE (frame.size() < nearly_too_big);
562 /* Place it in a bigger block with some zero padding at the end */
563 dcp::ArrayData oversized_frame(nearly_too_big);
564 memcpy (oversized_frame.data(), frame.data(), frame.size());
565 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
567 boost::filesystem::path const dir("build/test/verify_test16");
568 boost::filesystem::remove_all (dir);
569 dcp_from_frame (oversized_frame, dir);
571 check_verify_result (
573 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES }}
578 /* DCP with a within-range JPEG2000 frame */
579 BOOST_AUTO_TEST_CASE (verify_test17)
581 /* Compress a black image */
582 auto image = black_image ();
583 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
584 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
586 boost::filesystem::path const dir("build/test/verify_test17");
587 boost::filesystem::remove_all (dir);
588 dcp_from_frame (frame, dir);
590 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
591 BOOST_REQUIRE_EQUAL (notes.size(), 0);
595 /* DCP with valid Interop subtitles */
596 BOOST_AUTO_TEST_CASE (verify_test18)
598 boost::filesystem::path const dir("build/test/verify_test18");
599 prepare_directory (dir);
600 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
601 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
602 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
603 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
605 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE }});
609 /* DCP with broken Interop subtitles */
610 BOOST_AUTO_TEST_CASE (verify_test19)
612 boost::filesystem::path const dir("build/test/verify_test19");
613 prepare_directory (dir);
614 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
615 auto asset = make_shared<dcp::InteropSubtitleAsset>(dir / "subs.xml");
616 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
617 write_dcp_with_single_asset (dir, reel_asset, dcp::INTEROP);
620 Editor e (dir / "subs.xml");
621 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
624 check_verify_result (
627 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::NOT_SMPTE },
628 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
629 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR }
634 /* DCP with valid SMPTE subtitles */
635 BOOST_AUTO_TEST_CASE (verify_test20)
637 boost::filesystem::path const dir("build/test/verify_test20");
638 prepare_directory (dir);
639 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
640 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
641 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
642 write_dcp_with_single_asset (dir, reel_asset);
644 check_verify_result ({dir}, {});
648 /* DCP with broken SMPTE subtitles */
649 BOOST_AUTO_TEST_CASE (verify_test21)
651 boost::filesystem::path const dir("build/test/verify_test21");
652 prepare_directory (dir);
653 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
654 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
655 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
656 write_dcp_with_single_asset (dir, reel_asset);
658 check_verify_result (
661 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
662 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
663 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
669 BOOST_AUTO_TEST_CASE (verify_test22)
671 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
672 prepare_directory (ov_dir);
674 auto image = black_image ();
675 auto frame = dcp::compress_j2k (image, 100000000, 24, false, false);
676 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
677 dcp_from_frame (frame, ov_dir);
679 dcp::DCP ov (ov_dir);
682 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
683 prepare_directory (vf_dir);
685 write_dcp_with_single_asset (vf_dir, ov.cpls().front()->reels().front()->main_picture());
687 check_verify_result (
689 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::EXTERNAL_ASSET }});
693 /* DCP with valid CompositionMetadataAsset */
694 BOOST_AUTO_TEST_CASE (verify_test23)
696 boost::filesystem::path const dir("build/test/verify_test23");
697 prepare_directory (dir);
699 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
700 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
701 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
703 auto reel = make_shared<dcp::Reel>();
704 reel->add (reel_asset);
706 reel->add (simple_markers(16 * 24 - 1));
708 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
710 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
711 cpl->set_main_sound_sample_rate (48000);
712 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
713 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
717 dcp.write_xml (dcp::SMPTE);
719 check_verify_result ({dir}, {});
723 boost::filesystem::path find_cpl (boost::filesystem::path dir)
725 for (auto i: boost::filesystem::directory_iterator(dir)) {
726 if (boost::starts_with(i.path().filename().string(), "cpl_")) {
731 BOOST_REQUIRE (false);
736 /* DCP with invalid CompositionMetadataAsset */
737 BOOST_AUTO_TEST_CASE (verify_test24)
739 boost::filesystem::path const dir("build/test/verify_test24");
740 prepare_directory (dir);
742 auto reel = make_shared<dcp::Reel>();
743 reel->add (black_picture_asset(dir));
744 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
746 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
747 cpl->set_main_sound_sample_rate (48000);
748 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
749 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
751 reel->add (simple_markers());
755 dcp.write_xml (dcp::SMPTE);
758 Editor e (find_cpl("build/test/verify_test24"));
759 e.replace ("MainSound", "MainSoundX");
762 check_verify_result (
765 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
766 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
767 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::XML_VALIDATION_ERROR },
768 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
773 /* DCP with invalid CompositionMetadataAsset */
774 BOOST_AUTO_TEST_CASE (verify_test25)
776 boost::filesystem::path const dir("build/test/verify_test25");
777 prepare_directory (dir);
779 auto reel = make_shared<dcp::Reel>();
780 reel->add (black_picture_asset(dir));
781 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
783 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
784 cpl->set_main_sound_sample_rate (48000);
785 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
786 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
790 dcp.write_xml (dcp::SMPTE);
793 Editor e (find_cpl("build/test/verify_test25"));
794 e.replace ("meta:Width", "meta:WidthX");
797 check_verify_result (
799 {{ dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::GENERAL_READ }}
804 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
805 BOOST_AUTO_TEST_CASE (verify_test26)
807 boost::filesystem::path const dir("build/test/verify_test26");
808 prepare_directory (dir);
809 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
810 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
811 asset->_language = "wrong-andbad";
812 asset->write (dir / "subs.mxf");
813 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
814 reel_asset->_language = "badlang";
815 write_dcp_with_single_asset (dir, reel_asset);
817 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
818 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
819 auto i = notes.begin();
820 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
821 BOOST_REQUIRE (i->note());
822 BOOST_CHECK_EQUAL (*i->note(), "badlang");
824 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
825 BOOST_REQUIRE (i->note());
826 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
830 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
831 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
833 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
834 prepare_directory (dir);
835 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
836 auto asset = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.mxf");
837 asset->_language = "wrong-andbad";
838 asset->write (dir / "subs.mxf");
839 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
840 reel_asset->_language = "badlang";
841 write_dcp_with_single_asset (dir, reel_asset);
843 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
844 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
845 auto i = notes.begin ();
846 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
847 BOOST_REQUIRE (i->note());
848 BOOST_CHECK_EQUAL (*i->note(), "badlang");
850 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
851 BOOST_REQUIRE (i->note());
852 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
856 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
857 * the release territory.
859 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
861 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
862 prepare_directory (dir);
864 auto picture = simple_picture (dir, "foo");
865 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
866 auto reel = make_shared<dcp::Reel>();
867 reel->add (reel_picture);
868 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
869 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
870 reel->add (reel_sound);
871 reel->add (simple_markers());
873 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
875 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
876 cpl->_additional_subtitle_languages.push_back("andso-is-this");
877 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
878 cpl->set_main_sound_sample_rate (48000);
879 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
880 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
881 cpl->_release_territory = "fred-jim";
882 auto dcp = make_shared<dcp::DCP>(dir);
884 dcp->write_xml (dcp::SMPTE);
886 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
887 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
888 auto i = notes.begin ();
889 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
890 BOOST_REQUIRE (i->note());
891 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
893 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
894 BOOST_REQUIRE (i->note());
895 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
897 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
898 BOOST_REQUIRE (i->note());
899 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
901 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
902 BOOST_REQUIRE (i->note());
903 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
909 vector<dcp::VerificationNote>
910 check_picture_size (int width, int height, int frame_rate, bool three_d)
912 using namespace boost::filesystem;
914 path dcp_path = "build/test/verify_picture_test";
915 remove_all (dcp_path);
916 create_directories (dcp_path);
918 shared_ptr<dcp::PictureAsset> mp;
920 mp = make_shared<dcp::StereoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
922 mp = make_shared<dcp::MonoPictureAsset>(dcp::Fraction(frame_rate, 1), dcp::SMPTE);
924 auto picture_writer = mp->start_write (dcp_path / "video.mxf", false);
926 auto image = black_image (dcp::Size(width, height));
927 auto j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
928 int const length = three_d ? frame_rate * 2 : frame_rate;
929 for (int i = 0; i < length; ++i) {
930 picture_writer->write (j2c.data(), j2c.size());
932 picture_writer->finalize ();
934 auto d = make_shared<dcp::DCP>(dcp_path);
935 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
936 cpl->set_annotation_text ("A Test DCP");
937 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
939 auto reel = make_shared<dcp::Reel>();
942 reel->add (make_shared<dcp::ReelStereoPictureAsset>(std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp), 0));
944 reel->add (make_shared<dcp::ReelMonoPictureAsset>(std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp), 0));
947 reel->add (simple_markers(frame_rate));
952 d->write_xml (dcp::SMPTE);
954 return dcp::verify ({dcp_path}, &stage, &progress, xsd_test);
960 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
962 auto notes = check_picture_size(width, height, frame_rate, three_d);
964 BOOST_CHECK_EQUAL (notes.size(), 0U);
970 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
972 auto notes = check_picture_size(width, height, frame_rate, three_d);
973 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
974 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
975 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
981 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
983 auto notes = check_picture_size(width, height, frame_rate, three_d);
984 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
985 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
986 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
992 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
994 auto notes = check_picture_size(width, height, frame_rate, three_d);
995 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
996 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
997 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1001 BOOST_AUTO_TEST_CASE (verify_picture_size)
1003 using namespace boost::filesystem;
1006 check_picture_size_ok (2048, 858, 24, false);
1007 check_picture_size_ok (2048, 858, 25, false);
1008 check_picture_size_ok (2048, 858, 48, false);
1009 check_picture_size_ok (2048, 858, 24, true);
1010 check_picture_size_ok (2048, 858, 25, true);
1011 check_picture_size_ok (2048, 858, 48, true);
1014 check_picture_size_ok (1998, 1080, 24, false);
1015 check_picture_size_ok (1998, 1080, 25, false);
1016 check_picture_size_ok (1998, 1080, 48, false);
1017 check_picture_size_ok (1998, 1080, 24, true);
1018 check_picture_size_ok (1998, 1080, 25, true);
1019 check_picture_size_ok (1998, 1080, 48, true);
1022 check_picture_size_ok (4096, 1716, 24, false);
1025 check_picture_size_ok (3996, 2160, 24, false);
1027 /* Bad frame size */
1028 check_picture_size_bad_frame_size (2050, 858, 24, false);
1029 check_picture_size_bad_frame_size (2048, 658, 25, false);
1030 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1031 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1033 /* Bad 2K frame rate */
1034 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1035 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1036 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1038 /* Bad 4K frame rate */
1039 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1040 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1043 auto notes = check_picture_size(3996, 2160, 24, true);
1044 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1045 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1046 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1052 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame, float v_position = 0, string text = "Hello")
1055 make_shared<dcp::SubtitleString>(
1063 dcp::Time(start_frame, 24, 24),
1064 dcp::Time(end_frame, 24, 24),
1080 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1082 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1083 prepare_directory (dir);
1085 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1086 for (int i = 0; i < 2048; ++i) {
1087 add_test_subtitle (asset, i * 24, i * 24 + 20);
1089 asset->set_language (dcp::LanguageTag("de-DE"));
1090 asset->write (dir / "subs.mxf");
1091 auto reel_asset = make_shared<dcp::ReelClosedCaptionAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1092 write_dcp_with_single_asset (dir, reel_asset);
1094 check_verify_result (
1097 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1098 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES },
1099 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1105 shared_ptr<dcp::SMPTESubtitleAsset>
1106 make_large_subtitle_asset (boost::filesystem::path font_file)
1108 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1109 dcp::ArrayData big_fake_font(1024 * 1024);
1110 big_fake_font.write (font_file);
1111 for (int i = 0; i < 116; ++i) {
1112 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1120 verify_timed_text_asset_too_large (string name)
1122 auto const dir = boost::filesystem::path("build/test") / name;
1123 prepare_directory (dir);
1124 auto asset = make_large_subtitle_asset (dir / "font.ttf");
1125 add_test_subtitle (asset, 0, 20);
1126 asset->set_language (dcp::LanguageTag("de-DE"));
1127 asset->write (dir / "subs.mxf");
1129 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1130 write_dcp_with_single_asset (dir, reel_asset);
1132 check_verify_result (
1135 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES },
1136 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES },
1137 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1138 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1143 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1145 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1146 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1150 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1152 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1153 prepare_directory (dir);
1154 auto dcp = make_simple (dir, 1, 240);
1157 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1158 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1159 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1160 "<ContentTitleText>Content</ContentTitleText>"
1161 "<AnnotationText>Annotation</AnnotationText>"
1162 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1163 "<ReelNumber>1</ReelNumber>"
1164 "<EditRate>25 1</EditRate>"
1165 "<TimeCodeRate>25</TimeCodeRate>"
1166 "<StartTime>00:00:00:00</StartTime>"
1167 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1169 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1170 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1171 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1177 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1178 BOOST_REQUIRE (xml_file);
1179 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1181 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1182 subs->write (dir / "subs.mxf");
1184 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1185 dcp->cpls().front()->reels().front()->add(reel_subs);
1186 dcp->write_xml (dcp::SMPTE);
1188 check_verify_result (
1191 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE },
1192 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1197 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1199 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1200 auto dcp = make_simple (path, 2, 240);
1201 auto cpl = dcp->cpls()[0];
1204 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1205 subs->set_language (dcp::LanguageTag("de-DE"));
1206 subs->add (simple_subtitle());
1207 subs->write (path / "subs1.mxf");
1208 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1209 cpl->reels()[0]->add(reel_subs);
1213 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1214 subs->set_language (dcp::LanguageTag("en-US"));
1215 subs->add (simple_subtitle());
1216 subs->write (path / "subs2.mxf");
1217 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1218 cpl->reels()[1]->add(reel_subs);
1221 dcp->write_xml (dcp::SMPTE);
1223 check_verify_result (
1226 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1227 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_LANGUAGES_DIFFER },
1228 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME }
1233 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1235 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1236 prepare_directory (dir);
1237 auto dcp = make_simple (dir, 1, 240);
1240 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1241 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1242 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1243 "<ContentTitleText>Content</ContentTitleText>"
1244 "<AnnotationText>Annotation</AnnotationText>"
1245 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1246 "<ReelNumber>1</ReelNumber>"
1247 "<Language>de-DE</Language>"
1248 "<EditRate>25 1</EditRate>"
1249 "<TimeCodeRate>25</TimeCodeRate>"
1250 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1252 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1253 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1254 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1260 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1261 BOOST_REQUIRE (xml_file);
1262 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1264 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1265 subs->write (dir / "subs.mxf");
1267 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1268 dcp->cpls().front()->reels().front()->add(reel_subs);
1269 dcp->write_xml (dcp::SMPTE);
1271 check_verify_result (
1274 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_SUBTITLE_START_TIME },
1275 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1280 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1282 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1283 prepare_directory (dir);
1284 auto dcp = make_simple (dir, 1, 240);
1287 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1288 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1289 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1290 "<ContentTitleText>Content</ContentTitleText>"
1291 "<AnnotationText>Annotation</AnnotationText>"
1292 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1293 "<ReelNumber>1</ReelNumber>"
1294 "<Language>de-DE</Language>"
1295 "<EditRate>25 1</EditRate>"
1296 "<TimeCodeRate>25</TimeCodeRate>"
1297 "<StartTime>00:00:02:00</StartTime>"
1298 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1300 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1301 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1302 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1308 auto xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1309 BOOST_REQUIRE (xml_file);
1310 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1312 auto subs = make_shared<dcp::SMPTESubtitleAsset>(dir / "subs.xml");
1313 subs->write (dir / "subs.mxf");
1315 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1316 dcp->cpls().front()->reels().front()->add(reel_subs);
1317 dcp->write_xml (dcp::SMPTE);
1319 check_verify_result (
1322 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO },
1323 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }
1331 TestText (int in_, int out_, float v_position_ = 0, string text_ = "Hello")
1334 , v_position(v_position_)
1347 dcp_with_text (boost::filesystem::path dir, vector<TestText> subs)
1349 prepare_directory (dir);
1350 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1351 asset->set_start_time (dcp::Time());
1352 for (auto i: subs) {
1353 add_test_subtitle (asset, i.in, i.out, i.v_position, i.text);
1355 asset->set_language (dcp::LanguageTag("de-DE"));
1356 asset->write (dir / "subs.mxf");
1358 auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1359 write_dcp_with_single_asset (dir, reel_asset);
1363 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1365 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1366 /* Just too early */
1367 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24 - 1, 5 * 24 }});
1368 check_verify_result (
1370 {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::FIRST_TEXT_TOO_EARLY }});
1374 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1376 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1377 /* Just late enough */
1378 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 5 * 24 }});
1379 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1380 BOOST_REQUIRE (notes.empty());
1384 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1386 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1387 prepare_directory (dir);
1389 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1390 asset1->set_start_time (dcp::Time());
1391 /* Just late enough */
1392 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1393 asset1->set_language (dcp::LanguageTag("de-DE"));
1394 asset1->write (dir / "subs1.mxf");
1395 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1396 auto reel1 = make_shared<dcp::Reel>();
1397 reel1->add (reel_asset1);
1398 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1399 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1400 reel1->add (markers1);
1402 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1403 asset2->set_start_time (dcp::Time());
1404 /* This would be too early on first reel but should be OK on the second */
1405 add_test_subtitle (asset2, 0, 4 * 24);
1406 asset2->set_language (dcp::LanguageTag("de-DE"));
1407 asset2->write (dir / "subs2.mxf");
1408 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1409 auto reel2 = make_shared<dcp::Reel>();
1410 reel2->add (reel_asset2);
1411 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 16 * 24, 0);
1412 markers2->set (dcp::Marker::LFOC, dcp::Time(16 * 24 - 1, 24, 24));
1413 reel2->add (markers2);
1415 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1418 auto dcp = make_shared<dcp::DCP>(dir);
1420 dcp->write_xml (dcp::SMPTE);
1422 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1423 BOOST_REQUIRE (notes.empty());
1427 BOOST_AUTO_TEST_CASE (verify_text_too_close)
1429 auto const dir = boost::filesystem::path("build/test/verify_text_too_close");
1430 dcp_with_text<dcp::ReelSubtitleAsset> (
1434 { 5 * 24 + 1, 6 * 24 },
1436 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_CLOSE }});
1440 BOOST_AUTO_TEST_CASE (verify_text_not_too_close)
1442 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_close");
1443 dcp_with_text<dcp::ReelSubtitleAsset> (
1447 { 5 * 24 + 16, 8 * 24 },
1449 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1450 BOOST_REQUIRE (notes.empty());
1454 BOOST_AUTO_TEST_CASE (verify_text_too_short)
1456 auto const dir = boost::filesystem::path("build/test/verify_text_too_short");
1457 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 1 }});
1458 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_TOO_SHORT }});
1462 BOOST_AUTO_TEST_CASE (verify_text_not_too_short)
1464 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_short");
1465 dcp_with_text<dcp::ReelSubtitleAsset> (dir, {{ 4 * 24, 4 * 24 + 17 }});
1466 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1467 BOOST_REQUIRE (notes.empty());
1471 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines1)
1473 auto const dir = boost::filesystem::path ("build/test/verify_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" },
1480 { 96, 200, 0.3, "lines" }
1482 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES}});
1486 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines1)
1488 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines1");
1489 dcp_with_text<dcp::ReelSubtitleAsset> (
1492 { 96, 200, 0.0, "We" },
1493 { 96, 200, 0.1, "have" },
1494 { 96, 200, 0.2, "four" },
1496 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1497 BOOST_REQUIRE (notes.empty());
1501 BOOST_AUTO_TEST_CASE (verify_too_many_subtitle_lines2)
1503 auto const dir = boost::filesystem::path ("build/test/verify_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 { 150, 180, 0.3, "lines" }
1512 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::TOO_MANY_SUBTITLE_LINES}});
1516 BOOST_AUTO_TEST_CASE (verify_not_too_many_subtitle_lines2)
1518 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_subtitle_lines2");
1519 dcp_with_text<dcp::ReelSubtitleAsset> (
1522 { 96, 300, 0.0, "We" },
1523 { 96, 300, 0.1, "have" },
1524 { 150, 180, 0.2, "four" },
1525 { 190, 250, 0.3, "lines" }
1527 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1528 BOOST_REQUIRE (notes.empty());
1532 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long1)
1534 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long1");
1535 dcp_with_text<dcp::ReelSubtitleAsset> (
1538 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123" }
1540 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_LONGER_THAN_RECOMMENDED }});
1544 BOOST_AUTO_TEST_CASE (verify_subtitle_lines_too_long2)
1546 auto const dir = boost::filesystem::path ("build/test/verify_subtitle_lines_too_long2");
1547 dcp_with_text<dcp::ReelSubtitleAsset> (
1550 { 96, 300, 0.0, "012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
1552 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::SUBTITLE_LINE_TOO_LONG }});
1556 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines1)
1558 auto const dir = boost::filesystem::path ("build/test/verify_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" },
1565 { 96, 200, 0.3, "lines" }
1567 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES}});
1571 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines1)
1573 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines1");
1574 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1577 { 96, 200, 0.0, "We" },
1578 { 96, 200, 0.1, "have" },
1579 { 96, 200, 0.2, "four" },
1581 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1582 BOOST_REQUIRE (notes.empty());
1586 BOOST_AUTO_TEST_CASE (verify_too_many_closed_caption_lines2)
1588 auto const dir = boost::filesystem::path ("build/test/verify_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 { 150, 180, 0.3, "lines" }
1597 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::TOO_MANY_CLOSED_CAPTION_LINES}});
1601 BOOST_AUTO_TEST_CASE (verify_not_too_many_closed_caption_lines2)
1603 auto const dir = boost::filesystem::path ("build/test/verify_not_too_many_closed_caption_lines2");
1604 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1607 { 96, 300, 0.0, "We" },
1608 { 96, 300, 0.1, "have" },
1609 { 150, 180, 0.2, "four" },
1610 { 190, 250, 0.3, "lines" }
1612 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1613 BOOST_REQUIRE (notes.empty());
1617 BOOST_AUTO_TEST_CASE (verify_closed_caption_lines_too_long1)
1619 auto const dir = boost::filesystem::path ("build/test/verify_closed_caption_lines_too_long1");
1620 dcp_with_text<dcp::ReelClosedCaptionAsset> (
1623 { 96, 300, 0.0, "0123456789012345678901234567890123" }
1625 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_LINE_TOO_LONG }});
1629 BOOST_AUTO_TEST_CASE (verify_sound_sampling_rate_must_be_48k)
1631 boost::filesystem::path const dir("build/test/verify_sound_sampling_rate_must_be_48k");
1632 prepare_directory (dir);
1634 auto picture = simple_picture (dir, "foo");
1635 auto reel_picture = make_shared<dcp::ReelMonoPictureAsset>(picture, 0);
1636 auto reel = make_shared<dcp::Reel>();
1637 reel->add (reel_picture);
1638 auto sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "de-DE", 24, 96000);
1639 auto reel_sound = make_shared<dcp::ReelSoundAsset>(sound, 0);
1640 reel->add (reel_sound);
1641 reel->add (simple_markers());
1642 auto cpl = make_shared<dcp::CPL>("hello", dcp::TRAILER);
1644 auto dcp = make_shared<dcp::DCP>(dir);
1646 dcp->write_xml (dcp::SMPTE);
1648 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::INVALID_SOUND_FRAME_RATE }});
1652 BOOST_AUTO_TEST_CASE (verify_cpl_must_have_annotation_text)
1654 boost::filesystem::path const dir("build/test/verify_cpl_must_have_annotation_text");
1655 auto dcp = make_simple (dir);
1656 dcp->write_xml (dcp::SMPTE);
1657 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1660 BOOST_REQUIRE (dcp->cpls()[0]->file());
1661 Editor e(dcp->cpls()[0]->file().get());
1662 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "");
1665 check_verify_result (
1668 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_ANNOTATION_TEXT_IN_CPL },
1669 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1674 BOOST_AUTO_TEST_CASE (verify_cpl_annotation_text_should_be_same_as_content_title_text)
1676 boost::filesystem::path const dir("build/test/verify_cpl_annotation_text_should_be_same_as_content_title_text");
1677 auto dcp = make_simple (dir);
1678 dcp->write_xml (dcp::SMPTE);
1679 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1682 BOOST_REQUIRE (dcp->cpls()[0]->file());
1683 Editor e(dcp->cpls()[0]->file().get());
1684 e.replace("<AnnotationText>A Test DCP</AnnotationText>", "<AnnotationText>A Test DCP 1</AnnotationText>");
1687 check_verify_result (
1690 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::CPL_ANNOTATION_TEXT_DIFFERS_FROM_CONTENT_TITLE_TEXT },
1691 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT }
1696 BOOST_AUTO_TEST_CASE (verify_reel_assets_durations_must_match)
1698 boost::filesystem::path const dir("build/test/verify_reel_assets_durations_must_match");
1699 boost::filesystem::remove_all (dir);
1700 boost::filesystem::create_directories (dir);
1701 shared_ptr<dcp::DCP> dcp (new dcp::DCP(dir));
1702 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::TRAILER));
1704 shared_ptr<dcp::MonoPictureAsset> mp = simple_picture (dir, "", 24);
1705 shared_ptr<dcp::SoundAsset> ms = simple_sound (dir, "", dcp::MXFMetadata(), "en-US", 25);
1707 auto reel = make_shared<dcp::Reel>(
1708 make_shared<dcp::ReelMonoPictureAsset>(mp, 0),
1709 make_shared<dcp::ReelSoundAsset>(ms, 0)
1712 reel->add (simple_markers());
1716 dcp->write_xml (dcp::SMPTE);
1718 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISMATCHED_ASSET_DURATION }});
1725 verify_subtitles_must_be_in_all_reels_check (boost::filesystem::path dir, bool add_to_reel1, bool add_to_reel2)
1727 boost::filesystem::remove_all (dir);
1728 boost::filesystem::create_directories (dir);
1729 auto dcp = make_shared<dcp::DCP>(dir);
1730 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1732 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1733 subs->set_language (dcp::LanguageTag("de-DE"));
1734 subs->set_start_time (dcp::Time());
1735 subs->add (simple_subtitle());
1736 subs->write (dir / "subs.mxf");
1737 auto reel_subs = make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0);
1739 auto reel1 = make_shared<dcp::Reel>(
1740 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1741 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1745 reel1->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1748 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1749 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1750 reel1->add (markers1);
1754 auto reel2 = make_shared<dcp::Reel>(
1755 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1756 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1760 reel2->add (make_shared<dcp::ReelSubtitleAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1763 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1764 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1765 reel2->add (markers2);
1770 dcp->write_xml (dcp::SMPTE);
1774 BOOST_AUTO_TEST_CASE (verify_subtitles_must_be_in_all_reels)
1777 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1778 verify_subtitles_must_be_in_all_reels_check (dir, true, false);
1779 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MAIN_SUBTITLE_NOT_IN_ALL_REELS}});
1783 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels2");
1784 verify_subtitles_must_be_in_all_reels_check (dir, true, true);
1785 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1786 BOOST_REQUIRE (notes.empty());
1790 boost::filesystem::path dir ("build/test/verify_subtitles_must_be_in_all_reels1");
1791 verify_subtitles_must_be_in_all_reels_check (dir, false, false);
1792 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1793 BOOST_REQUIRE (notes.empty());
1800 verify_closed_captions_must_be_in_all_reels_check (boost::filesystem::path dir, int caps_in_reel1, int caps_in_reel2)
1802 boost::filesystem::remove_all (dir);
1803 boost::filesystem::create_directories (dir);
1804 auto dcp = make_shared<dcp::DCP>(dir);
1805 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1807 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1808 subs->set_language (dcp::LanguageTag("de-DE"));
1809 subs->set_start_time (dcp::Time());
1810 subs->add (simple_subtitle());
1811 subs->write (dir / "subs.mxf");
1813 auto reel1 = make_shared<dcp::Reel>(
1814 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1815 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1818 for (int i = 0; i < caps_in_reel1; ++i) {
1819 reel1->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1822 auto markers1 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1823 markers1->set (dcp::Marker::FFOC, dcp::Time(1, 24, 24));
1824 reel1->add (markers1);
1828 auto reel2 = make_shared<dcp::Reel>(
1829 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1830 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1833 for (int i = 0; i < caps_in_reel2; ++i) {
1834 reel2->add (make_shared<dcp::ReelClosedCaptionAsset>(subs, dcp::Fraction(24, 1), 240, 0));
1837 auto markers2 = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 240, 0);
1838 markers2->set (dcp::Marker::LFOC, dcp::Time(239, 24, 24));
1839 reel2->add (markers2);
1844 dcp->write_xml (dcp::SMPTE);
1849 BOOST_AUTO_TEST_CASE (verify_closed_captions_must_be_in_all_reels)
1852 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels1");
1853 verify_closed_captions_must_be_in_all_reels_check (dir, 3, 4);
1854 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::CLOSED_CAPTION_ASSET_COUNTS_DIFFER }});
1858 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels2");
1859 verify_closed_captions_must_be_in_all_reels_check (dir, 4, 4);
1860 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1861 BOOST_REQUIRE (notes.empty());
1865 boost::filesystem::path dir ("build/test/verify_closed_captions_must_be_in_all_reels3");
1866 verify_closed_captions_must_be_in_all_reels_check (dir, 0, 0);
1867 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1868 BOOST_REQUIRE (notes.empty());
1875 verify_text_entry_point_check (boost::filesystem::path dir, dcp::VerificationNote::Code code, boost::function<void (shared_ptr<T>)> adjust)
1877 boost::filesystem::remove_all (dir);
1878 boost::filesystem::create_directories (dir);
1879 auto dcp = make_shared<dcp::DCP>(dir);
1880 auto cpl = make_shared<dcp::CPL>("A Test DCP", dcp::TRAILER);
1882 auto subs = make_shared<dcp::SMPTESubtitleAsset>();
1883 subs->set_language (dcp::LanguageTag("de-DE"));
1884 subs->set_start_time (dcp::Time());
1885 subs->add (simple_subtitle());
1886 subs->write (dir / "subs.mxf");
1887 auto reel_text = make_shared<T>(subs, dcp::Fraction(24, 1), 240, 0);
1890 auto reel = make_shared<dcp::Reel>(
1891 make_shared<dcp::ReelMonoPictureAsset>(simple_picture(dir, "", 240), 0),
1892 make_shared<dcp::ReelSoundAsset>(simple_sound(dir, "", dcp::MXFMetadata(), "en-US", 240), 0)
1895 reel->add (reel_text);
1897 reel->add (simple_markers(240));
1902 dcp->write_xml (dcp::SMPTE);
1904 check_verify_result ({dir}, {{ dcp::VerificationNote::VERIFY_BV21_ERROR, code }});
1908 BOOST_AUTO_TEST_CASE (verify_text_entry_point)
1910 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1911 "build/test/verify_subtitle_entry_point_must_be_present",
1912 dcp::VerificationNote::MISSING_SUBTITLE_ENTRY_POINT,
1913 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
1914 asset->unset_entry_point ();
1918 verify_text_entry_point_check<dcp::ReelSubtitleAsset> (
1919 "build/test/verify_subtitle_entry_point_must_be_zero",
1920 dcp::VerificationNote::SUBTITLE_ENTRY_POINT_NON_ZERO,
1921 [](shared_ptr<dcp::ReelSubtitleAsset> asset) {
1922 asset->set_entry_point (4);
1926 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
1927 "build/test/verify_closed_caption_entry_point_must_be_present",
1928 dcp::VerificationNote::MISSING_CLOSED_CAPTION_ENTRY_POINT,
1929 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
1930 asset->unset_entry_point ();
1934 verify_text_entry_point_check<dcp::ReelClosedCaptionAsset> (
1935 "build/test/verify_closed_caption_entry_point_must_be_zero",
1936 dcp::VerificationNote::CLOSED_CAPTION_ENTRY_POINT_NON_ZERO,
1937 [](shared_ptr<dcp::ReelClosedCaptionAsset> asset) {
1938 asset->set_entry_point (9);
1944 BOOST_AUTO_TEST_CASE (verify_assets_must_have_hashes)
1948 boost::filesystem::path const dir("build/test/verify_assets_must_have_hashes");
1949 auto dcp = make_simple (dir);
1950 dcp->write_xml (dcp::SMPTE);
1951 BOOST_REQUIRE_EQUAL (dcp->cpls().size(), 1U);
1954 BOOST_REQUIRE (dcp->cpls()[0]->file());
1955 Editor e(dcp->cpls()[0]->file().get());
1956 e.replace("<Hash>cb1OLhgHG9svy7G8hoTSPpltzhw=</Hash>", "");
1959 check_verify_result (
1962 { dcp::VerificationNote::VERIFY_ERROR, dcp::VerificationNote::CPL_HASH_INCORRECT },
1963 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_HASH }
1970 verify_markers_test (
1971 boost::filesystem::path dir,
1972 vector<pair<dcp::Marker, dcp::Time>> markers,
1973 vector<std::pair<dcp::VerificationNote::Type, dcp::VerificationNote::Code>> types_and_codes
1976 auto dcp = make_simple (dir);
1977 dcp->cpls()[0]->set_content_kind (dcp::FEATURE);
1978 auto markers_asset = make_shared<dcp::ReelMarkersAsset>(dcp::Fraction(24, 1), 24, 0);
1979 for (auto const& i: markers) {
1980 markers_asset->set (i.first, i.second);
1982 dcp->cpls()[0]->reels()[0]->add(markers_asset);
1983 dcp->write_xml (dcp::SMPTE);
1984 check_verify_result ({dir}, types_and_codes);
1988 BOOST_AUTO_TEST_CASE (verify_markers)
1990 verify_markers_test (
1991 "build/test/verify_markers_all_correct",
1993 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
1994 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
1995 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
1996 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2001 verify_markers_test (
2002 "build/test/verify_markers_missing_ffec",
2004 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2005 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2006 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2009 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFEC_IN_FEATURE }
2012 verify_markers_test (
2013 "build/test/verify_markers_missing_ffmc",
2015 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2016 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2017 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2020 { dcp::VerificationNote::VERIFY_BV21_ERROR, dcp::VerificationNote::MISSING_FFMC_IN_FEATURE }
2023 verify_markers_test (
2024 "build/test/verify_markers_missing_ffoc",
2026 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2027 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2028 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2031 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_FFOC}
2034 verify_markers_test (
2035 "build/test/verify_markers_missing_lfoc",
2037 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2038 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2039 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) }
2042 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::MISSING_LFOC }
2045 verify_markers_test (
2046 "build/test/verify_markers_incorrect_ffoc",
2048 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2049 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2050 { dcp::Marker::FFOC, dcp::Time(3, 24, 24) },
2051 { dcp::Marker::LFOC, dcp::Time(23, 24, 24) }
2054 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_FFOC }
2057 verify_markers_test (
2058 "build/test/verify_markers_incorrect_lfoc",
2060 { dcp::Marker::FFEC, dcp::Time(12, 24, 24) },
2061 { dcp::Marker::FFMC, dcp::Time(13, 24, 24) },
2062 { dcp::Marker::FFOC, dcp::Time(1, 24, 24) },
2063 { dcp::Marker::LFOC, dcp::Time(18, 24, 24) }
2066 { dcp::VerificationNote::VERIFY_WARNING, dcp::VerificationNote::INCORRECT_LFOC }