2 Copyright (C) 2018-2020 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
38 #include "reel_mono_picture_asset.h"
39 #include "reel_sound_asset.h"
42 #include "openjpeg_image.h"
43 #include "mono_picture_asset.h"
44 #include "stereo_picture_asset.h"
45 #include "mono_picture_asset_writer.h"
46 #include "interop_subtitle_asset.h"
47 #include "smpte_subtitle_asset.h"
48 #include "reel_closed_caption_asset.h"
49 #include "reel_stereo_picture_asset.h"
50 #include "reel_subtitle_asset.h"
51 #include "compose.hpp"
53 #include <boost/test/unit_test.hpp>
54 #include <boost/foreach.hpp>
55 #include <boost/algorithm/string.hpp>
64 using std::make_shared;
65 using boost::optional;
66 using std::shared_ptr;
69 static list<pair<string, optional<boost::filesystem::path> > > stages;
72 stage (string s, optional<boost::filesystem::path> p)
74 stages.push_back (make_pair (s, p));
84 prepare_directory (boost::filesystem::path path)
86 using namespace boost::filesystem;
88 create_directories (path);
92 static vector<boost::filesystem::path>
93 setup (int reference_number, int verify_test_number)
95 prepare_directory (dcp::String::compose("build/test/verify_test%1", verify_test_number));
96 for (boost::filesystem::directory_iterator i(dcp::String::compose("test/ref/DCP/dcp_test%1", reference_number)); i != boost::filesystem::directory_iterator(); ++i) {
97 boost::filesystem::copy_file (i->path(), dcp::String::compose("build/test/verify_test%1", verify_test_number) / i->path().filename());
100 vector<boost::filesystem::path> directories;
101 directories.push_back (dcp::String::compose("build/test/verify_test%1", verify_test_number));
107 /** Class that can alter a file by searching and replacing strings within it.
108 * On destruction modifies the file whose name was given to the constructor.
113 Editor (boost::filesystem::path path)
116 _content = dcp::file_to_string (_path);
121 FILE* f = fopen(_path.string().c_str(), "w");
123 fwrite (_content.c_str(), _content.length(), 1, f);
127 void replace (string a, string b)
129 boost::algorithm::replace_all (_content, a, b);
133 boost::filesystem::path _path;
134 std::string _content;
140 dump_notes (list<dcp::VerificationNote> const & notes)
142 BOOST_FOREACH (dcp::VerificationNote i, notes) {
143 std::cout << dcp::note_to_string(i) << "\n";
147 /* Check DCP as-is (should be OK) */
148 BOOST_AUTO_TEST_CASE (verify_test1)
151 vector<boost::filesystem::path> directories = setup (1, 1);
152 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
154 boost::filesystem::path const cpl_file = "build/test/verify_test1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml";
155 boost::filesystem::path const pkl_file = "build/test/verify_test1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml";
156 boost::filesystem::path const assetmap_file = "build/test/verify_test1/ASSETMAP.xml";
158 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
159 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
160 BOOST_REQUIRE (st->second);
161 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1"));
163 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
164 BOOST_REQUIRE (st->second);
165 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
167 BOOST_CHECK_EQUAL (st->first, "Checking reel");
168 BOOST_REQUIRE (!st->second);
170 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
171 BOOST_REQUIRE (st->second);
172 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
174 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
175 BOOST_REQUIRE (st->second);
176 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/video.mxf"));
178 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
179 BOOST_REQUIRE (st->second);
180 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
182 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
183 BOOST_REQUIRE (st->second);
184 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test1/audio.mxf"));
186 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
187 BOOST_REQUIRE (st->second);
188 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
190 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
191 BOOST_REQUIRE (st->second);
192 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
194 BOOST_REQUIRE (st == stages.end());
196 BOOST_CHECK_EQUAL (notes.size(), 0);
199 /* Corrupt the MXFs and check that this is spotted */
200 BOOST_AUTO_TEST_CASE (verify_test2)
202 vector<boost::filesystem::path> directories = setup (1, 2);
204 FILE* mod = fopen("build/test/verify_test2/video.mxf", "r+b");
206 fseek (mod, 4096, SEEK_SET);
208 fwrite (&x, sizeof(x), 1, mod);
211 mod = fopen("build/test/verify_test2/audio.mxf", "r+b");
213 BOOST_REQUIRE_EQUAL (fseek(mod, -64, SEEK_END), 0);
214 BOOST_REQUIRE (fwrite (&x, sizeof(x), 1, mod) == 1);
217 list<dcp::VerificationNote> notes;
219 dcp::ASDCPErrorSuspender sus;
220 notes = dcp::verify (directories, &stage, &progress, xsd_test);
223 BOOST_REQUIRE_EQUAL (notes.size(), 2);
224 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
225 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_HASH_INCORRECT);
226 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_ERROR);
227 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::SOUND_HASH_INCORRECT);
230 /* Corrupt the hashes in the PKL and check that the disagreement between CPL and PKL is spotted */
231 BOOST_AUTO_TEST_CASE (verify_test3)
233 vector<boost::filesystem::path> directories = setup (1, 3);
236 Editor e ("build/test/verify_test3/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml");
237 e.replace ("<Hash>", "<Hash>x");
240 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
242 BOOST_REQUIRE_EQUAL (notes.size(), 6);
243 list<dcp::VerificationNote>::const_iterator i = notes.begin();
244 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
245 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
247 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
248 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_PICTURE_HASHES_DIFFER);
250 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
251 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::PKL_CPL_SOUND_HASHES_DIFFER);
253 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
254 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
256 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
257 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
259 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_ERROR);
260 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
264 /* Corrupt the ContentKind in the CPL */
265 BOOST_AUTO_TEST_CASE (verify_test4)
267 vector<boost::filesystem::path> directories = setup (1, 4);
270 Editor e ("build/test/verify_test4/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml");
271 e.replace ("<ContentKind>", "<ContentKind>x");
274 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
276 BOOST_REQUIRE_EQUAL (notes.size(), 1);
277 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::GENERAL_READ);
278 BOOST_CHECK_EQUAL (*notes.front().note(), "Bad content kind 'xfeature'");
282 boost::filesystem::path
285 return dcp::String::compose("build/test/verify_test%1/cpl_81fb54df-e1bf-4647-8788-ea7ba154375b.xml", n);
289 boost::filesystem::path
292 return dcp::String::compose("build/test/verify_test%1/pkl_cd49971e-bf4c-4594-8474-54ebef09a40c.xml", n);
296 boost::filesystem::path
299 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
303 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1)
305 vector<boost::filesystem::path> directories = setup (1, n);
309 e.replace (from, to);
312 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
314 BOOST_REQUIRE_EQUAL (notes.size(), 1);
315 BOOST_CHECK_EQUAL (notes.front().code(), code1);
319 void check_after_replace (int n, boost::function<boost::filesystem::path (int)> file, string from, string to, dcp::VerificationNote::Code code1, dcp::VerificationNote::Code code2)
321 vector<boost::filesystem::path> directories = setup (1, n);
325 e.replace (from, to);
328 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
330 BOOST_REQUIRE_EQUAL (notes.size(), 2);
331 BOOST_CHECK_EQUAL (notes.front().code(), code1);
332 BOOST_CHECK_EQUAL (notes.back().code(), code2);
336 void check_after_replace (
337 int n, boost::function<boost::filesystem::path (int)> file,
340 dcp::VerificationNote::Code code1,
341 dcp::VerificationNote::Code code2,
342 dcp::VerificationNote::Code code3
345 vector<boost::filesystem::path> directories = setup (1, n);
349 e.replace (from, to);
352 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
354 BOOST_REQUIRE_EQUAL (notes.size(), 3);
355 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
356 BOOST_CHECK_EQUAL (i->code(), code1);
358 BOOST_CHECK_EQUAL (i->code(), code2);
360 BOOST_CHECK_EQUAL (i->code(), code3);
364 BOOST_AUTO_TEST_CASE (verify_test5)
366 check_after_replace (
368 "<FrameRate>24 1", "<FrameRate>99 1",
369 dcp::VerificationNote::CPL_HASH_INCORRECT,
370 dcp::VerificationNote::INVALID_PICTURE_FRAME_RATE
375 BOOST_AUTO_TEST_CASE (verify_test6)
377 vector<boost::filesystem::path> directories = setup (1, 6);
379 boost::filesystem::remove ("build/test/verify_test6/video.mxf");
380 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
382 BOOST_REQUIRE_EQUAL (notes.size(), 1);
383 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_ERROR);
384 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::MISSING_ASSET);
388 boost::filesystem::path
391 return dcp::String::compose("build/test/verify_test%1/ASSETMAP.xml", n);
394 /* Empty asset filename in ASSETMAP */
395 BOOST_AUTO_TEST_CASE (verify_test7)
397 check_after_replace (
399 "<Path>video.mxf</Path>", "<Path></Path>",
400 dcp::VerificationNote::EMPTY_ASSET_PATH
404 /* Mismatched standard */
405 BOOST_AUTO_TEST_CASE (verify_test8)
407 check_after_replace (
409 "http://www.smpte-ra.org/schemas/429-7/2006/CPL", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#",
410 dcp::VerificationNote::MISMATCHED_STANDARD,
411 dcp::VerificationNote::XML_VALIDATION_ERROR,
412 dcp::VerificationNote::CPL_HASH_INCORRECT
416 /* Badly formatted <Id> in CPL */
417 BOOST_AUTO_TEST_CASE (verify_test9)
419 /* There's no CPL_HASH_INCORRECT error here because it can't find the correct hash by ID (since the ID is wrong) */
420 check_after_replace (
422 "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b", "<Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375",
423 dcp::VerificationNote::XML_VALIDATION_ERROR
427 /* Badly formatted <IssueDate> in CPL */
428 BOOST_AUTO_TEST_CASE (verify_test10)
430 check_after_replace (
432 "<IssueDate>", "<IssueDate>x",
433 dcp::VerificationNote::XML_VALIDATION_ERROR,
434 dcp::VerificationNote::CPL_HASH_INCORRECT
438 /* Badly-formatted <Id> in PKL */
439 BOOST_AUTO_TEST_CASE (verify_test11)
441 check_after_replace (
443 "<Id>urn:uuid:cd4", "<Id>urn:uuid:xd4",
444 dcp::VerificationNote::XML_VALIDATION_ERROR
448 /* Badly-formatted <Id> in ASSETMAP */
449 BOOST_AUTO_TEST_CASE (verify_test12)
451 check_after_replace (
453 "<Id>urn:uuid:63c", "<Id>urn:uuid:x3c",
454 dcp::VerificationNote::XML_VALIDATION_ERROR
458 /* Basic test of an Interop DCP */
459 BOOST_AUTO_TEST_CASE (verify_test13)
462 vector<boost::filesystem::path> directories = setup (3, 13);
463 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
465 boost::filesystem::path const cpl_file = "build/test/verify_test13/cpl_cbfd2bc0-21cf-4a8f-95d8-9cddcbe51296.xml";
466 boost::filesystem::path const pkl_file = "build/test/verify_test13/pkl_d87a950c-bd6f-41f6-90cc-56ccd673e131.xml";
467 boost::filesystem::path const assetmap_file = "build/test/verify_test13/ASSETMAP";
469 list<pair<string, optional<boost::filesystem::path> > >::const_iterator st = stages.begin();
470 BOOST_CHECK_EQUAL (st->first, "Checking DCP");
471 BOOST_REQUIRE (st->second);
472 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13"));
474 BOOST_CHECK_EQUAL (st->first, "Checking CPL");
475 BOOST_REQUIRE (st->second);
476 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(cpl_file));
478 BOOST_CHECK_EQUAL (st->first, "Checking reel");
479 BOOST_REQUIRE (!st->second);
481 BOOST_CHECK_EQUAL (st->first, "Checking picture asset hash");
482 BOOST_REQUIRE (st->second);
483 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
485 BOOST_CHECK_EQUAL (st->first, "Checking picture frame sizes");
486 BOOST_REQUIRE (st->second);
487 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/j2c_c6035f97-b07d-4e1c-944d-603fc2ddc242.mxf"));
489 BOOST_CHECK_EQUAL (st->first, "Checking sound asset hash");
490 BOOST_REQUIRE (st->second);
491 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
493 BOOST_CHECK_EQUAL (st->first, "Checking sound asset metadata");
494 BOOST_REQUIRE (st->second);
495 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical("build/test/verify_test13/pcm_69cf9eaf-9a99-4776-b022-6902208626c3.mxf"));
497 BOOST_CHECK_EQUAL (st->first, "Checking PKL");
498 BOOST_REQUIRE (st->second);
499 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(pkl_file));
501 BOOST_CHECK_EQUAL (st->first, "Checking ASSETMAP");
502 BOOST_REQUIRE (st->second);
503 BOOST_CHECK_EQUAL (st->second.get(), boost::filesystem::canonical(assetmap_file));
505 BOOST_REQUIRE (st == stages.end());
507 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
508 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
509 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
510 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
513 /* DCP with a short asset */
514 BOOST_AUTO_TEST_CASE (verify_test14)
516 vector<boost::filesystem::path> directories = setup (8, 14);
517 list<dcp::VerificationNote> notes = dcp::verify (directories, &stage, &progress, xsd_test);
519 BOOST_REQUIRE_EQUAL (notes.size(), 5);
520 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
521 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
523 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
525 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
527 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::DURATION_TOO_SMALL);
529 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::INTRINSIC_DURATION_TOO_SMALL);
536 dcp_from_frame (dcp::ArrayData const& frame, boost::filesystem::path dir)
538 shared_ptr<dcp::MonoPictureAsset> asset(new dcp::MonoPictureAsset(dcp::Fraction(24, 1), dcp::SMPTE));
539 boost::filesystem::create_directories (dir);
540 shared_ptr<dcp::PictureAssetWriter> writer = asset->start_write (dir / "pic.mxf", true);
541 for (int i = 0; i < 24; ++i) {
542 writer->write (frame.data(), frame.size());
546 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelMonoPictureAsset(asset, 0));
547 shared_ptr<dcp::Reel> reel(new dcp::Reel());
548 reel->add (reel_asset);
549 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
551 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
553 dcp->write_xml (dcp::SMPTE);
557 /* DCP with an over-sized JPEG2000 frame */
558 BOOST_AUTO_TEST_CASE (verify_test15)
560 int const too_big = 1302083 * 2;
562 /* Compress a black image */
563 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
564 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
565 BOOST_REQUIRE (frame.size() < too_big);
567 /* Place it in a bigger block with some zero padding at the end */
568 dcp::ArrayData oversized_frame(too_big);
569 memcpy (oversized_frame.data(), frame.data(), frame.size());
570 memset (oversized_frame.data() + frame.size(), 0, too_big - frame.size());
572 boost::filesystem::path const dir("build/test/verify_test15");
573 boost::filesystem::remove_all (dir);
574 dcp_from_frame (oversized_frame, dir);
576 vector<boost::filesystem::path> dirs;
577 dirs.push_back (dir);
578 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
579 BOOST_REQUIRE_EQUAL (notes.size(), 1);
580 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_TOO_LARGE_IN_BYTES);
584 /* DCP with a nearly over-sized JPEG2000 frame */
585 BOOST_AUTO_TEST_CASE (verify_test16)
587 int const nearly_too_big = 1302083 * 0.98;
589 /* Compress a black image */
590 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
591 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
592 BOOST_REQUIRE (frame.size() < nearly_too_big);
594 /* Place it in a bigger block with some zero padding at the end */
595 dcp::ArrayData oversized_frame(nearly_too_big);
596 memcpy (oversized_frame.data(), frame.data(), frame.size());
597 memset (oversized_frame.data() + frame.size(), 0, nearly_too_big - frame.size());
599 boost::filesystem::path const dir("build/test/verify_test16");
600 boost::filesystem::remove_all (dir);
601 dcp_from_frame (oversized_frame, dir);
603 vector<boost::filesystem::path> dirs;
604 dirs.push_back (dir);
605 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
606 BOOST_REQUIRE_EQUAL (notes.size(), 1);
607 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_FRAME_NEARLY_TOO_LARGE_IN_BYTES);
611 /* DCP with a within-range JPEG2000 frame */
612 BOOST_AUTO_TEST_CASE (verify_test17)
614 /* Compress a black image */
615 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
616 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
617 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
619 boost::filesystem::path const dir("build/test/verify_test17");
620 boost::filesystem::remove_all (dir);
621 dcp_from_frame (frame, dir);
623 vector<boost::filesystem::path> dirs;
624 dirs.push_back (dir);
625 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
626 BOOST_REQUIRE_EQUAL (notes.size(), 0);
630 /* DCP with valid Interop subtitles */
631 BOOST_AUTO_TEST_CASE (verify_test18)
633 boost::filesystem::path const dir("build/test/verify_test18");
634 prepare_directory (dir);
635 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
636 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
637 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
638 shared_ptr<dcp::Reel> reel(new dcp::Reel());
639 reel->add (reel_asset);
640 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
642 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
644 dcp->write_xml (dcp::INTEROP);
646 vector<boost::filesystem::path> dirs;
647 dirs.push_back (dir);
648 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
649 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
650 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
651 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
655 /* DCP with broken Interop subtitles */
656 BOOST_AUTO_TEST_CASE (verify_test19)
658 boost::filesystem::path const dir("build/test/verify_test19");
659 prepare_directory (dir);
660 boost::filesystem::copy_file ("test/data/subs1.xml", dir / "subs.xml");
661 shared_ptr<dcp::InteropSubtitleAsset> asset(new dcp::InteropSubtitleAsset(dir / "subs.xml"));
662 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
663 shared_ptr<dcp::Reel> reel(new dcp::Reel());
664 reel->add (reel_asset);
665 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
667 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
669 dcp->write_xml (dcp::INTEROP);
672 Editor e (dir / "subs.xml");
673 e.replace ("</ReelNumber>", "</ReelNumber><Foo></Foo>");
676 vector<boost::filesystem::path> dirs;
677 dirs.push_back (dir);
678 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
679 BOOST_REQUIRE_EQUAL (notes.size(), 3);
680 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
681 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::NOT_SMPTE);
683 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
685 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
690 /* DCP with valid SMPTE subtitles */
691 BOOST_AUTO_TEST_CASE (verify_test20)
693 boost::filesystem::path const dir("build/test/verify_test20");
694 prepare_directory (dir);
695 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
696 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
697 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
698 shared_ptr<dcp::Reel> reel(new dcp::Reel());
699 reel->add (reel_asset);
700 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
702 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
704 dcp->write_xml (dcp::SMPTE);
706 vector<boost::filesystem::path> dirs;
707 dirs.push_back (dir);
708 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
710 BOOST_REQUIRE_EQUAL (notes.size(), 0);
714 /* DCP with broken SMPTE subtitles */
715 BOOST_AUTO_TEST_CASE (verify_test21)
717 boost::filesystem::path const dir("build/test/verify_test21");
718 prepare_directory (dir);
719 boost::filesystem::copy_file ("test/data/broken_smpte.mxf", dir / "subs.mxf");
720 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
721 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
722 shared_ptr<dcp::Reel> reel(new dcp::Reel());
723 reel->add (reel_asset);
724 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
726 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
728 dcp->write_xml (dcp::SMPTE);
730 vector<boost::filesystem::path> dirs;
731 dirs.push_back (dir);
732 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
733 BOOST_REQUIRE_EQUAL (notes.size(), 3);
734 list<dcp::VerificationNote>::const_iterator i = notes.begin();
735 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
737 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
739 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
744 BOOST_AUTO_TEST_CASE (verify_test22)
746 boost::filesystem::path const ov_dir("build/test/verify_test22_ov");
747 prepare_directory (ov_dir);
749 shared_ptr<dcp::OpenJPEGImage> image = black_image ();
750 dcp::ArrayData frame = dcp::compress_j2k (image, 100000000, 24, false, false);
751 BOOST_REQUIRE (frame.size() < 230000000 / (24 * 8));
752 dcp_from_frame (frame, ov_dir);
754 dcp::DCP ov (ov_dir);
757 boost::filesystem::path const vf_dir("build/test/verify_test22_vf");
758 prepare_directory (vf_dir);
760 shared_ptr<dcp::Reel> reel(new dcp::Reel());
761 reel->add (ov.cpls().front()->reels().front()->main_picture());
762 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
764 dcp::DCP vf (vf_dir);
766 vf.write_xml (dcp::SMPTE);
768 vector<boost::filesystem::path> dirs;
769 dirs.push_back (vf_dir);
770 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
771 BOOST_REQUIRE_EQUAL (notes.size(), 1);
772 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::EXTERNAL_ASSET);
776 /* DCP with valid CompositionMetadataAsset */
777 BOOST_AUTO_TEST_CASE (verify_test23)
779 boost::filesystem::path const dir("build/test/verify_test23");
780 prepare_directory (dir);
782 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
783 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
784 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
786 shared_ptr<dcp::Reel> reel(new dcp::Reel());
787 reel->add (reel_asset);
788 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
790 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
791 cpl->set_main_sound_sample_rate (48000);
792 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
793 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
797 dcp.write_xml (dcp::SMPTE);
799 vector<boost::filesystem::path> dirs;
800 dirs.push_back (dir);
801 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
805 boost::filesystem::path find_cpl (boost::filesystem::path dir)
807 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); i++) {
808 if (boost::starts_with(i->path().filename().string(), "cpl_")) {
813 BOOST_REQUIRE (false);
814 return boost::filesystem::path();
818 /* DCP with invalid CompositionMetadataAsset */
819 BOOST_AUTO_TEST_CASE (verify_test24)
821 boost::filesystem::path const dir("build/test/verify_test24");
822 prepare_directory (dir);
824 shared_ptr<dcp::Reel> reel(new dcp::Reel());
825 reel->add (black_picture_asset(dir));
826 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
828 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
829 cpl->set_main_sound_sample_rate (48000);
830 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
831 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
835 dcp.write_xml (dcp::SMPTE);
838 Editor e (find_cpl("build/test/verify_test24"));
839 e.replace ("MainSound", "MainSoundX");
842 vector<boost::filesystem::path> dirs;
843 dirs.push_back (dir);
844 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
845 BOOST_REQUIRE_EQUAL (notes.size(), 4);
847 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
848 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
850 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
852 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::XML_VALIDATION_ERROR);
854 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CPL_HASH_INCORRECT);
859 /* DCP with invalid CompositionMetadataAsset */
860 BOOST_AUTO_TEST_CASE (verify_test25)
862 boost::filesystem::path const dir("build/test/verify_test25");
863 prepare_directory (dir);
865 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
866 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
867 shared_ptr<dcp::ReelAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
869 shared_ptr<dcp::Reel> reel(new dcp::Reel());
870 reel->add (reel_asset);
871 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
873 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
874 cpl->set_main_sound_sample_rate (48000);
875 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
876 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
880 dcp.write_xml (dcp::SMPTE);
883 Editor e (find_cpl("build/test/verify_test25"));
884 e.replace ("</MainPictureActiveArea>", "</MainPictureActiveArea><BadTag></BadTag>");
887 vector<boost::filesystem::path> dirs;
888 dirs.push_back (dir);
889 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
893 /* SMPTE DCP with invalid <Language> in the MainSubtitle reel and also in the XML within the MXF */
894 BOOST_AUTO_TEST_CASE (verify_test26)
896 boost::filesystem::path const dir("build/test/verify_test26");
897 prepare_directory (dir);
898 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
899 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
900 asset->_language = "wrong-andbad";
901 asset->write (dir / "subs.mxf");
902 shared_ptr<dcp::ReelSubtitleAsset> reel_asset(new dcp::ReelSubtitleAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
903 reel_asset->_language = "badlang";
904 shared_ptr<dcp::Reel> reel(new dcp::Reel());
905 reel->add (reel_asset);
906 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
908 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
910 dcp->write_xml (dcp::SMPTE);
912 vector<boost::filesystem::path> dirs;
913 dirs.push_back (dir);
914 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
915 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
916 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
917 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
918 BOOST_REQUIRE (i->note());
919 BOOST_CHECK_EQUAL (*i->note(), "badlang");
921 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
922 BOOST_REQUIRE (i->note());
923 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
927 /* SMPTE DCP with invalid <Language> in the MainClosedCaption reel and also in the XML within the MXF */
928 BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_languages)
930 boost::filesystem::path const dir("build/test/verify_invalid_closed_caption_languages");
931 prepare_directory (dir);
932 boost::filesystem::copy_file ("test/data/subs.mxf", dir / "subs.mxf");
933 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset(dir / "subs.mxf"));
934 asset->_language = "wrong-andbad";
935 asset->write (dir / "subs.mxf");
936 shared_ptr<dcp::ReelClosedCaptionAsset> reel_asset(new dcp::ReelClosedCaptionAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
937 reel_asset->_language = "badlang";
938 shared_ptr<dcp::Reel> reel(new dcp::Reel());
939 reel->add (reel_asset);
940 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
942 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
944 dcp->write_xml (dcp::SMPTE);
946 vector<boost::filesystem::path> dirs;
947 dirs.push_back (dir);
948 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
949 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
950 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
951 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
952 BOOST_REQUIRE (i->note());
953 BOOST_CHECK_EQUAL (*i->note(), "badlang");
955 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
956 BOOST_REQUIRE (i->note());
957 BOOST_CHECK_EQUAL (*i->note(), "wrong-andbad");
961 /* SMPTE DCP with invalid <Language> in the MainSound reel, the CPL additional subtitles languages and
962 * the release territory.
964 BOOST_AUTO_TEST_CASE (verify_various_invalid_languages)
966 boost::filesystem::path const dir("build/test/verify_various_invalid_languages");
967 prepare_directory (dir);
969 shared_ptr<dcp::MonoPictureAsset> picture = simple_picture (dir, "foo");
970 shared_ptr<dcp::ReelPictureAsset> reel_picture(new dcp::ReelMonoPictureAsset(picture, 0));
971 shared_ptr<dcp::Reel> reel(new dcp::Reel());
972 reel->add (reel_picture);
973 shared_ptr<dcp::SoundAsset> sound = simple_sound (dir, "foo", dcp::MXFMetadata(), "frobozz");
974 shared_ptr<dcp::ReelSoundAsset> reel_sound(new dcp::ReelSoundAsset(sound, 0));
975 reel->add (reel_sound);
976 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
978 cpl->_additional_subtitle_languages.push_back("this-is-wrong");
979 cpl->_additional_subtitle_languages.push_back("andso-is-this");
980 cpl->set_main_sound_configuration ("L,C,R,Lfe,-,-");
981 cpl->set_main_sound_sample_rate (48000);
982 cpl->set_main_picture_stored_area (dcp::Size(1998, 1080));
983 cpl->set_main_picture_active_area (dcp::Size(1440, 1080));
984 cpl->_release_territory = "fred-jim";
985 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
987 dcp->write_xml (dcp::SMPTE);
989 vector<boost::filesystem::path> dirs;
990 dirs.push_back (dir);
991 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
992 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
993 list<dcp::VerificationNote>::const_iterator i = notes.begin ();
994 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
995 BOOST_REQUIRE (i->note());
996 BOOST_CHECK_EQUAL (*i->note(), "this-is-wrong");
998 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
999 BOOST_REQUIRE (i->note());
1000 BOOST_CHECK_EQUAL (*i->note(), "andso-is-this");
1002 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
1003 BOOST_REQUIRE (i->note());
1004 BOOST_CHECK_EQUAL (*i->note(), "fred-jim");
1006 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::BAD_LANGUAGE);
1007 BOOST_REQUIRE (i->note());
1008 BOOST_CHECK_EQUAL (*i->note(), "frobozz");
1014 list<dcp::VerificationNote>
1015 check_picture_size (int width, int height, int frame_rate, bool three_d)
1017 using namespace boost::filesystem;
1019 path dcp_path = "build/test/verify_picture_test";
1020 remove_all (dcp_path);
1021 create_directories (dcp_path);
1023 shared_ptr<dcp::PictureAsset> mp;
1025 mp.reset (new dcp::StereoPictureAsset(dcp::Fraction(frame_rate, 1), dcp::SMPTE));
1027 mp.reset (new dcp::MonoPictureAsset(dcp::Fraction(frame_rate, 1), dcp::SMPTE));
1029 shared_ptr<dcp::PictureAssetWriter> picture_writer = mp->start_write (dcp_path / "video.mxf", false);
1031 shared_ptr<dcp::OpenJPEGImage> image = black_image (dcp::Size(width, height));
1032 dcp::ArrayData j2c = dcp::compress_j2k (image, 100000000, frame_rate, three_d, width > 2048);
1033 int const length = three_d ? frame_rate * 2 : frame_rate;
1034 for (int i = 0; i < length; ++i) {
1035 picture_writer->write (j2c.data(), j2c.size());
1037 picture_writer->finalize ();
1039 shared_ptr<dcp::DCP> d (new dcp::DCP(dcp_path));
1040 shared_ptr<dcp::CPL> cpl (new dcp::CPL("A Test DCP", dcp::FEATURE));
1041 cpl->set_annotation_text ("A Test DCP");
1042 cpl->set_issue_date ("2012-07-17T04:45:18+00:00");
1044 shared_ptr<dcp::Reel> reel(new dcp::Reel());
1048 shared_ptr<dcp::ReelPictureAsset>(
1049 new dcp::ReelStereoPictureAsset(
1050 std::dynamic_pointer_cast<dcp::StereoPictureAsset>(mp),
1056 shared_ptr<dcp::ReelPictureAsset>(
1057 new dcp::ReelMonoPictureAsset(
1058 std::dynamic_pointer_cast<dcp::MonoPictureAsset>(mp),
1067 d->write_xml (dcp::SMPTE);
1069 vector<boost::filesystem::path> dirs;
1070 dirs.push_back (dcp_path);
1071 return dcp::verify (dirs, &stage, &progress, xsd_test);
1077 check_picture_size_ok (int width, int height, int frame_rate, bool three_d)
1079 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1081 BOOST_CHECK_EQUAL (notes.size(), 0U);
1087 check_picture_size_bad_frame_size (int width, int height, int frame_rate, bool three_d)
1089 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1090 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1091 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1092 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_SIZE_IN_PIXELS);
1098 check_picture_size_bad_2k_frame_rate (int width, int height, int frame_rate, bool three_d)
1100 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1101 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1102 BOOST_CHECK_EQUAL (notes.back().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1103 BOOST_CHECK_EQUAL (notes.back().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_2K);
1109 check_picture_size_bad_4k_frame_rate (int width, int height, int frame_rate, bool three_d)
1111 list<dcp::VerificationNote> notes = check_picture_size(width, height, frame_rate, three_d);
1112 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1113 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1114 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_INVALID_FRAME_RATE_FOR_4K);
1118 BOOST_AUTO_TEST_CASE (verify_picture_size)
1120 using namespace boost::filesystem;
1123 check_picture_size_ok (2048, 858, 24, false);
1124 check_picture_size_ok (2048, 858, 25, false);
1125 check_picture_size_ok (2048, 858, 48, false);
1126 check_picture_size_ok (2048, 858, 24, true);
1127 check_picture_size_ok (2048, 858, 25, true);
1128 check_picture_size_ok (2048, 858, 48, true);
1131 check_picture_size_ok (1998, 1080, 24, false);
1132 check_picture_size_ok (1998, 1080, 25, false);
1133 check_picture_size_ok (1998, 1080, 48, false);
1134 check_picture_size_ok (1998, 1080, 24, true);
1135 check_picture_size_ok (1998, 1080, 25, true);
1136 check_picture_size_ok (1998, 1080, 48, true);
1139 check_picture_size_ok (4096, 1716, 24, false);
1142 check_picture_size_ok (3996, 2160, 24, false);
1144 /* Bad frame size */
1145 check_picture_size_bad_frame_size (2050, 858, 24, false);
1146 check_picture_size_bad_frame_size (2048, 658, 25, false);
1147 check_picture_size_bad_frame_size (1920, 1080, 48, true);
1148 check_picture_size_bad_frame_size (4000, 3000, 24, true);
1150 /* Bad 2K frame rate */
1151 check_picture_size_bad_2k_frame_rate (2048, 858, 26, false);
1152 check_picture_size_bad_2k_frame_rate (2048, 858, 31, false);
1153 check_picture_size_bad_2k_frame_rate (1998, 1080, 50, true);
1155 /* Bad 4K frame rate */
1156 check_picture_size_bad_4k_frame_rate (3996, 2160, 25, false);
1157 check_picture_size_bad_4k_frame_rate (3996, 2160, 48, false);
1160 list<dcp::VerificationNote> notes = check_picture_size(3996, 2160, 24, true);
1161 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1162 BOOST_CHECK_EQUAL (notes.front().type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1163 BOOST_CHECK_EQUAL (notes.front().code(), dcp::VerificationNote::PICTURE_ASSET_4K_3D);
1169 add_test_subtitle (shared_ptr<dcp::SubtitleAsset> asset, int start_frame, int end_frame)
1172 make_shared<dcp::SubtitleString>(
1180 dcp::Time(start_frame, 24, 24),
1181 dcp::Time(end_frame, 24, 24),
1197 BOOST_AUTO_TEST_CASE (verify_closed_caption_xml_too_large)
1199 boost::filesystem::path const dir("build/test/verify_closed_caption_xml_too_large");
1200 prepare_directory (dir);
1202 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset());
1203 for (int i = 0; i < 2048; ++i) {
1204 add_test_subtitle (asset, i * 24, i * 24 + 20);
1206 asset->set_language (dcp::LanguageTag("de-DE"));
1207 asset->write (dir / "subs.mxf");
1208 shared_ptr<dcp::ReelClosedCaptionAsset> reel_asset(new dcp::ReelClosedCaptionAsset(asset, dcp::Fraction(24, 1), 16 * 24, 0));
1209 shared_ptr<dcp::Reel> reel(new dcp::Reel());
1210 reel->add (reel_asset);
1211 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
1213 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
1215 dcp->write_xml (dcp::SMPTE);
1217 vector<boost::filesystem::path> dirs;
1218 dirs.push_back (dir);
1219 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1220 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
1221 list<dcp::VerificationNote>::const_iterator i = notes.begin();
1222 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1224 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
1226 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1227 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::CLOSED_CAPTION_XML_TOO_LARGE_IN_BYTES);
1232 shared_ptr<dcp::SMPTESubtitleAsset>
1233 make_large_subtitle_asset (boost::filesystem::path font_file)
1235 shared_ptr<dcp::SMPTESubtitleAsset> asset(new dcp::SMPTESubtitleAsset());
1236 dcp::ArrayData big_fake_font(1024 * 1024);
1237 big_fake_font.write (font_file);
1238 for (int i = 0; i < 116; ++i) {
1239 asset->add_font (dcp::String::compose("big%1", i), big_fake_font);
1247 verify_timed_text_asset_too_large (string name)
1249 boost::filesystem::path const dir = boost::filesystem::path("build/test") / name;
1250 prepare_directory (dir);
1251 shared_ptr<dcp::SMPTESubtitleAsset> asset = make_large_subtitle_asset (dir / "font.ttf");
1252 add_test_subtitle (asset, 0, 20);
1253 asset->set_language (dcp::LanguageTag("de-DE"));
1254 asset->write (dir / "subs.mxf");
1256 shared_ptr<T> reel_asset(new T(asset, dcp::Fraction(24, 1), 16 * 24, 0));
1257 shared_ptr<dcp::Reel> reel(new dcp::Reel());
1258 reel->add (reel_asset);
1259 shared_ptr<dcp::CPL> cpl(new dcp::CPL("hello", dcp::FEATURE));
1261 shared_ptr<dcp::DCP> dcp(new dcp::DCP(dir));
1263 dcp->write_xml (dcp::SMPTE);
1265 vector<boost::filesystem::path> dirs;
1266 dirs.push_back (dir);
1267 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1268 BOOST_REQUIRE_EQUAL (notes.size(), 4U);
1270 list<dcp::VerificationNote>::const_iterator i = notes.begin();
1271 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1272 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::TIMED_TEXT_ASSET_TOO_LARGE_IN_BYTES);
1274 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1275 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::TIMED_TEXT_FONTS_TOO_LARGE_IN_BYTES);
1277 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1279 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
1283 BOOST_AUTO_TEST_CASE (verify_subtitle_asset_too_large)
1285 verify_timed_text_asset_too_large<dcp::ReelSubtitleAsset>("verify_subtitle_asset_too_large");
1286 verify_timed_text_asset_too_large<dcp::ReelClosedCaptionAsset>("verify_closed_caption_asset_too_large");
1290 BOOST_AUTO_TEST_CASE (verify_missing_language_tag_in_subtitle_xml)
1292 boost::filesystem::path dir = "build/test/verify_missing_language_tag_in_subtitle_xml";
1293 prepare_directory (dir);
1294 shared_ptr<dcp::DCP> dcp = make_simple (dir, 1);
1297 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1298 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1299 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1300 "<ContentTitleText>Content</ContentTitleText>"
1301 "<AnnotationText>Annotation</AnnotationText>"
1302 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1303 "<ReelNumber>1</ReelNumber>"
1304 "<EditRate>25 1</EditRate>"
1305 "<TimeCodeRate>25</TimeCodeRate>"
1306 "<StartTime>00:00:00:00</StartTime>"
1307 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1309 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1310 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1311 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1317 FILE* xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1318 BOOST_REQUIRE (xml_file);
1319 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1321 shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset(dir / "subs.xml"));
1322 subs->write (dir / "subs.mxf");
1324 shared_ptr<dcp::ReelSubtitleAsset> reel_subs (new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 100, 0));
1325 dcp->cpls().front()->reels().front()->add (reel_subs);
1326 dcp->write_xml (dcp::SMPTE);
1328 vector<boost::filesystem::path> dirs;
1329 dirs.push_back (dir);
1330 auto notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1331 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1332 auto i = notes.begin();
1333 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1334 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_LANGUAGE);
1336 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
1340 BOOST_AUTO_TEST_CASE (verify_inconsistent_subtitle_languages)
1342 boost::filesystem::path path ("build/test/verify_inconsistent_subtitle_languages");
1343 shared_ptr<dcp::DCP> dcp = make_simple (path, 2);
1344 shared_ptr<dcp::CPL> cpl = dcp->cpls().front();
1347 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
1348 subs->set_language (dcp::LanguageTag("de-DE"));
1349 subs->add (simple_subtitle());
1350 subs->write (path / "subs1.mxf");
1351 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
1352 cpl->reels().front()->add (reel_subs);
1356 shared_ptr<dcp::SMPTESubtitleAsset> subs(new dcp::SMPTESubtitleAsset());
1357 subs->set_language (dcp::LanguageTag("en-US"));
1358 subs->add (simple_subtitle());
1359 subs->write (path / "subs2.mxf");
1360 shared_ptr<dcp::ReelSubtitleAsset> reel_subs(new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 240, 0));
1361 cpl->reels().back()->add (reel_subs);
1364 dcp->write_xml (dcp::SMPTE);
1366 vector<boost::filesystem::path> dirs;
1367 dirs.push_back (path);
1368 list<dcp::VerificationNote> notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1369 BOOST_REQUIRE_EQUAL (notes.size(), 3U);
1370 list<dcp::VerificationNote>::const_iterator i = notes.begin();
1371 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1373 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1375 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1379 BOOST_AUTO_TEST_CASE (verify_missing_start_time_tag_in_subtitle_xml)
1381 boost::filesystem::path dir = "build/test/verify_missing_start_time_tag_in_subtitle_xml";
1382 prepare_directory (dir);
1383 shared_ptr<dcp::DCP> dcp = make_simple (dir, 1);
1386 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1387 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1388 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1389 "<ContentTitleText>Content</ContentTitleText>"
1390 "<AnnotationText>Annotation</AnnotationText>"
1391 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1392 "<ReelNumber>1</ReelNumber>"
1393 "<Language>de-DE</Language>"
1394 "<EditRate>25 1</EditRate>"
1395 "<TimeCodeRate>25</TimeCodeRate>"
1396 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1398 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1399 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1400 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1406 FILE* xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1407 BOOST_REQUIRE (xml_file);
1408 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1410 shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset(dir / "subs.xml"));
1411 subs->write (dir / "subs.mxf");
1413 shared_ptr<dcp::ReelSubtitleAsset> reel_subs (new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 100, 0));
1414 dcp->cpls().front()->reels().front()->add (reel_subs);
1415 dcp->write_xml (dcp::SMPTE);
1417 vector<boost::filesystem::path> dirs;
1418 dirs.push_back (dir);
1419 auto notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1420 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1421 auto i = notes.begin();
1422 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1423 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::MISSING_SUBTITLE_START_TIME);
1425 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
1429 BOOST_AUTO_TEST_CASE (verify_non_zero_start_time_tag_in_subtitle_xml)
1431 boost::filesystem::path dir = "build/test/verify_non_zero_start_time_tag_in_subtitle_xml";
1432 prepare_directory (dir);
1433 shared_ptr<dcp::DCP> dcp = make_simple (dir, 1);
1436 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
1437 "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/schema\">"
1438 "<Id>urn:uuid:e6a8ae03-ebbf-41ed-9def-913a87d1493a</Id>"
1439 "<ContentTitleText>Content</ContentTitleText>"
1440 "<AnnotationText>Annotation</AnnotationText>"
1441 "<IssueDate>2018-10-02T12:25:14+02:00</IssueDate>"
1442 "<ReelNumber>1</ReelNumber>"
1443 "<Language>de-DE</Language>"
1444 "<EditRate>25 1</EditRate>"
1445 "<TimeCodeRate>25</TimeCodeRate>"
1446 "<StartTime>00:00:02:00</StartTime>"
1447 "<LoadFont ID=\"arial\">urn:uuid:e4f0ff0a-9eba-49e0-92ee-d89a88a575f6</LoadFont>"
1449 "<Font ID=\"arial\" Color=\"FFFEFEFE\" Weight=\"normal\" Size=\"42\" Effect=\"border\" EffectColor=\"FF181818\" AspectAdjust=\"1.00\">"
1450 "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:03:00\" TimeOut=\"00:00:04:10\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">"
1451 "<Text Hposition=\"0.0\" Halign=\"center\" Valign=\"bottom\" Vposition=\"13.5\" Direction=\"ltr\">Hello world</Text>"
1457 FILE* xml_file = dcp::fopen_boost (dir / "subs.xml", "w");
1458 BOOST_REQUIRE (xml_file);
1459 fwrite (xml.c_str(), xml.size(), 1, xml_file);
1461 shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset(dir / "subs.xml"));
1462 subs->write (dir / "subs.mxf");
1464 shared_ptr<dcp::ReelSubtitleAsset> reel_subs (new dcp::ReelSubtitleAsset(subs, dcp::Fraction(24, 1), 100, 0));
1465 dcp->cpls().front()->reels().front()->add (reel_subs);
1466 dcp->write_xml (dcp::SMPTE);
1468 vector<boost::filesystem::path> dirs;
1469 dirs.push_back (dir);
1470 auto notes = dcp::verify (dirs, &stage, &progress, xsd_test);
1471 BOOST_REQUIRE_EQUAL (notes.size(), 2U);
1472 auto i = notes.begin();
1473 BOOST_CHECK_EQUAL (i->type(), dcp::VerificationNote::VERIFY_BV21_ERROR);
1474 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::SUBTITLE_START_TIME_NON_ZERO);
1476 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
1480 BOOST_AUTO_TEST_CASE (verify_text_too_early)
1482 auto const dir = boost::filesystem::path("build/test/verify_text_too_early");
1483 prepare_directory (dir);
1484 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1485 asset->set_start_time (dcp::Time());
1486 /* Just too early */
1487 add_test_subtitle (asset, 4 * 24 - 1, 5 * 24);
1488 asset->set_language (dcp::LanguageTag("de-DE"));
1489 asset->write (dir / "subs.mxf");
1491 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1492 auto reel = make_shared<dcp::Reel>();
1493 reel->add (reel_asset);
1494 auto cpl = make_shared<dcp::CPL>("hello", dcp::FEATURE);
1496 auto dcp = make_shared<dcp::DCP>(dir);
1498 dcp->write_xml (dcp::SMPTE);
1500 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1501 BOOST_REQUIRE_EQUAL (notes.size(), 1U);
1502 auto i = notes.begin();
1503 BOOST_CHECK_EQUAL (i->code(), dcp::VerificationNote::FIRST_TEXT_TOO_EARLY);
1507 BOOST_AUTO_TEST_CASE (verify_text_not_too_early)
1509 auto const dir = boost::filesystem::path("build/test/verify_text_not_too_early");
1510 prepare_directory (dir);
1511 auto asset = make_shared<dcp::SMPTESubtitleAsset>();
1512 asset->set_start_time (dcp::Time());
1513 /* Just late enough */
1514 add_test_subtitle (asset, 4 * 24, 5 * 24);
1515 asset->set_language (dcp::LanguageTag("de-DE"));
1516 asset->write (dir / "subs.mxf");
1518 auto reel_asset = make_shared<dcp::ReelSubtitleAsset>(asset, dcp::Fraction(24, 1), 16 * 24, 0);
1519 auto reel = make_shared<dcp::Reel>();
1520 reel->add (reel_asset);
1521 auto cpl = make_shared<dcp::CPL>("hello", dcp::FEATURE);
1523 auto dcp = make_shared<dcp::DCP>(dir);
1525 dcp->write_xml (dcp::SMPTE);
1527 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1529 BOOST_REQUIRE (notes.empty());
1533 BOOST_AUTO_TEST_CASE (verify_text_early_on_second_reel)
1535 auto const dir = boost::filesystem::path("build/test/verify_text_early_on_second_reel");
1536 prepare_directory (dir);
1538 auto asset1 = make_shared<dcp::SMPTESubtitleAsset>();
1539 asset1->set_start_time (dcp::Time());
1540 /* Just late enough */
1541 add_test_subtitle (asset1, 4 * 24, 5 * 24);
1542 asset1->set_language (dcp::LanguageTag("de-DE"));
1543 asset1->write (dir / "subs.mxf");
1544 auto reel_asset1 = make_shared<dcp::ReelSubtitleAsset>(asset1, dcp::Fraction(24, 1), 16 * 24, 0);
1545 auto reel1 = make_shared<dcp::Reel>();
1546 reel1->add (reel_asset1);
1548 auto asset2 = make_shared<dcp::SMPTESubtitleAsset>();
1549 asset2->set_start_time (dcp::Time());
1550 /* This would be too early on first reel but should be OK on the second */
1551 add_test_subtitle (asset2, 0, 4 * 24);
1552 asset2->set_language (dcp::LanguageTag("de-DE"));
1553 asset2->write (dir / "subs.mxf");
1554 auto reel_asset2 = make_shared<dcp::ReelSubtitleAsset>(asset2, dcp::Fraction(24, 1), 16 * 24, 0);
1555 auto reel2 = make_shared<dcp::Reel>();
1556 reel2->add (reel_asset2);
1558 auto cpl = make_shared<dcp::CPL>("hello", dcp::FEATURE);
1561 auto dcp = make_shared<dcp::DCP>(dir);
1563 dcp->write_xml (dcp::SMPTE);
1565 auto notes = dcp::verify ({dir}, &stage, &progress, xsd_test);
1567 BOOST_REQUIRE (notes.empty());